原题链接:圈圈
解题思路:
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分
2 人评分
C语言网提供由在职研发工程师或ACM蓝桥杯竞赛优秀选手录制的视频教程,并配有习题和答疑,点击了解:
一点编程也不会写的:零基础C语言学练课程
解决困扰你多年的C语言疑难杂症特性的C语言进阶课程
从零到写出一个爬虫的Python编程课程
只会语法写不出代码?手把手带你写100个编程真题的编程百练课程
信息学奥赛或C++选手的 必学C++课程
蓝桥杯ACM、信息学奥赛的必学课程:算法竞赛课入门课程
手把手讲解近五年真题的蓝桥杯辅导课程
发表评论 取消回复