“红旗杯“第十四届东北地区大学生程序设计竞赛-热身赛

本文探讨了Mo算法在解决复杂数据结构问题中的应用,包括如何高效地找到子序列的众数,以及如何判断一个数是否能由特定数列中的元素分割而成。通过实例解析算法实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Problem A

Problem Description
Mo’s algorithm can solve some difficult data structure problems. Like this: You are given an array a1,a2,…,an and m queries. In each query, we want to get the mode number(the value that appears most often in a set of data) of subsequence al,al+1,…,ar.

Maybe you want to solve it using segment tree or some other data structures. However, if you know the answer of [l,k] and [k+1,r], you will find that it is very difficult to get the answer of [l,r].

Mo’s algorithm is an offline algorithm that can solve this problem efficiently.

First, we need a data structure that can do these operations:

  1. Insert a number.
  2. Delete a number.
  3. Get the mode number.

It can be solved easily by a binary heap or a balanced binary search tree. We call this data structure DS.

On the other hand, if we only keep al,al+1,…,ar in DS, we can transform to [l′,r′] in |l−l′|+|r−r′| moves. So we need to find a good order to perform queries that the total number of moves is minimized.

It is very hard to find such excellent order. Mo’s algorithm just gives an order that the total number of moves is O(mn√).

In this task, you are given the length of the sequence n and m queries q1,q2,…,qm. Please write a program to rearrange these queries that cost(q1,q2)+cost(q2,q3)+⋯+cost(qm−1,qm) is minimized, where cost([l,r],[l′,r′])=|l−l′|+|r−r′|.

Input
The first line of the input contains an integer T(1≤T≤10), denoting the number of test cases.

In each test case, there are two integers n,m(1≤n≤100000,2≤m≤10) in the first line, denoting the length of the sequence and the number of queries.

For the next m lines, each line contains two integers li and ri(1≤li≤ri≤n), denoting a query.

Output
For each test case, print a single line containing an integer, denoting the minimum number of moves.

Sample Input
2
5 2
1 5
3 4
9 5
5 5
1 1
4 4
2 2
3 3

Sample Output
3
8

// 题意易猜得;;;一发过了。。。

题意:给你一个n长度的数组,给你m次查询机会,通过m次重新排序,使得m个的l,r差值最小,输出最小差值。

题解:全排列m个l,r,找出最小的差值,输出即可。m范围小,暴力即可。暴力排列所有情况

#include<iostream>
#include<stdio.h>
#include<algorithm>
using namespace std;
typedef long long ll;
const int maxn=110;
int t,n,m,k;
struct node{
	int l;
	int r;
};
node f[maxn];
int a[11];
int main()
{
	cin>>t;
	while(t--)
	{
		cin>>n>>m;
		for(int i=1;i<=m;i++)
			a[i]=i;
		for(int i=1;i<=m;i++)
		{
			scanf("%d%d",&f[i].l,&f[i].r);
		}
		int ans=0x3fffffff;
		do{
			int t=0;
			for(int i=2;i<=m;i++)
			{
				t+=abs(f[a[i]].l-f[a[i-1]].l);
				t+=abs(f[a[i]].r-f[a[i-1]].r);
			}
			ans=min(ans,t);
		}while(next_permutation(a+1,a+m+1));
		printf("%d\n",ans);
	}
	return 0;
}

Problem B

Problem Description
There is an infinite integer sequence f1,f2,f3,…, where f1=1,f2=2,fi=fi−1+fi−2(i≥3).

You are given two positive integers n and k, your task is to check whether n can be split into k integers a1,a2,…,ak, where a1,a2,…,ak∈f and a1+a2+⋯+ak=n. Note that you can split n into same integers, for example, 10=5+5=f4+f4.

Input
The first line of the input contains an integer T(1≤T≤100000), denoting the number of test cases.

In each test case, there is one integer n(1≤n,k≤1018) in the first line.

Output
For each test case, print a single line. If it is possible to split n into k integers, print Yes, otherwise print No.

Sample Input
4
10 1
10 2
5 1
12 2

Sample Output
No
Yes
Yes
No

题意:一个数列f1=1,f2=2;fn = fn-1 + fn-2;问n可否由k个数构成,能则输出yes,不能输出no

//dfs剪枝没剪好容易TLE。TLE两发,罚时40min,呜呜~

题解:由斐波那契数列构成可以,发现后面的数可以由前面的数组成,则计算出1e18,范围内所有函数值(f88时将会>1e18)。则减去后面最大值,消耗一次k,将n刚刚好消耗完,k>=0时即可输出yes(>0也可输出yes因为后面的大数,可拆分为前面的小的数(观察推理即可发现))。特殊情况判断,n==k输出yes,n < k 输出no

代码:

#include<iostream>
#include<stdio.h>
#include<algorithm>
using namespace std;
typedef long long ll;
const int maxn=110;
ll f[maxn];
ll t,n,m,k;
int main()
{
	cin>>t;
	f[1]=1;f[2]=2;
	for(int i=3;i<=100;i++)
	{
		f[i]=f[i-1]+f[i-2];
	}
	while(t--)
	{
		cin>>n>>k;
		if(n<k)
		{
			puts("No");
			continue;
		}
		if(n==k)
		{
			puts("Yes");
			continue;
		}
		while(n>0 && k>0)
		{
			for(int i=88;i>=1;i--)
			{
				if(n>=f[i] && k>0)
				{
					n-=f[i];
					k--;
					break;
				}
			}
		}
		if(n==0 && k>=0)
			puts("Yes");
		else
			puts("No");
	}
	return 0;
}

C题:
目测bfs,最短路,priority_queue解决。待补题(还是太菜了啊!!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值