解题思路:
1、首先序列的变化是由于队列中出现大于m的数字取模而改变序列的字典序最小而造成的,所以在序列中没有出现取模的时候序列不会变

2、确定最小字典序是通过找到最长公共序列来确定:比如101012的最小字典序为什么是第一个1而不是第三个1,此时我们寻找的最长公共子序列应该是10101,若第三个作为队首,此时得到的序列是121010,当然不是字典序最小。

3、通过二分枚举长度来找到最长的序列的长度

4、通过字符串哈希来存储目标序列,通过前缀和来寻找[l.r]之间的序列情况(类似于k进制数)
注意事项:

代码的初始化:1、最好是先去确定那些数字会因为+i而变化取模,这样会少很多次遍历寻找取模数

2、不用实现队列,我们只需要把数列长为n的复制一份(从n+1处)这样其实和队列的效果一样的。


参考代码

#include<iostream>
#include<vector>
using namespace std;
const int N=50010*2;
typedef unsigned long long ll;
long long base=131;
ll arr[N],ha[N],p[N];
int n,m,k,ans;
vector<int> v[N];
int find(int x,int y,int add)//当队列变化时,寻找x与y的最长公共子序列,返回下一次调整好的队首
{
	int l=0,r=n;//枚举区间为0~n,长度取中间
	while(l<r)
	{
		int mid = l+r+1>>1;//mid是枚举长度
		if(ha[x+mid-1]-ha[x-1]*p[mid]==ha[y+mid-1]-ha[y-1]*p[mid]) l=mid;//若公共序列相等,则说明枚举区间太端,需要拉长区间
		else r=mid-1;//否则压缩区间
	}
	if(l==n) return x;//区间长度是最大的n,此时说明以x与y开始的最长公共序列的长度是一样的,即返回x和返回y效果一样
	if((arr[x+l]+add)%m<=(arr[y+l]+add)%m) return x;//若长度没有达到n,说明x和y长度不一样,则需要比较下一位谁最小,小的则为下次队列的队首
	else return y;
}
int main()
{
	ios::sync_with_stdio(false);
	cin.tie(nullptr);
	cout.tie(nullptr);
	cin>>n>>m>>k;
	for(int i=1;i<=n;i++)
	{
		cin>>arr[i];
		arr[i+n]=arr[i];
		v[(m-arr[i])%m].push_back(i);
	}
	p[0]=1;
	for(int i=1;i<=2*n;i++) p[i]=p[i-1]*base;//进制数组,类似10的0次方为1,一次方为10,二次方为100
	for(int i=1;i<=2*n;i++) ha[i]=ha[i-1]*base+arr[i];//字符串哈希 将目标数组通过哈希数组来存 
	ans=1;
	for(int i=2;i<=n;i++)
	{
		ans= find(ans,i,0);//寻找第一次的队首
	}
	cout<<arr[ans+k-1]%m<<'\n';
	for(int i=1;i<m;i++)
	{
		if(v[i].size())
		{
			ans=v[i][0];
			for(int j=1;j<v[i].size();j++) ans=find(ans,v[i][j],i);
		}
		cout<<(arr[ans+k-1]+i)%m<<'\n';
	}
	return 0;
}

最后欢迎大佬指正

点赞(0)
 

0.0分

2 人评分

C语言网提供由在职研发工程师或ACM蓝桥杯竞赛优秀选手录制的视频教程,并配有习题和答疑,点击了解:

一点编程也不会写的:零基础C语言学练课程

解决困扰你多年的C语言疑难杂症特性的C语言进阶课程

从零到写出一个爬虫的Python编程课程

只会语法写不出代码?手把手带你写100个编程真题的编程百练课程

信息学奥赛或C++选手的 必学C++课程

蓝桥杯ACM、信息学奥赛的必学课程:算法竞赛课入门课程

手把手讲解近五年真题的蓝桥杯辅导课程

评论列表 共有 0 条评论

暂无评论