解题思路:
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分
12 人评分
1009题解浏览:802 |
1035 题解浏览:875 |
蓝桥杯历届试题-翻硬币 (C++代码)浏览:953 |
震宇大神的杀毒软件 (C语言代码)浏览:1162 |
淘淘的名单 (C语言代码)浏览:1309 |
理财计划 (C语言代码)浏览:494 |
判定字符位置 (C语言代码)浏览:849 |
C语言程序设计教程(第三版)课后习题6.7 (C语言代码)浏览:615 |
C二级辅导-统计字符 (C语言描述——用函数求解)浏览:1229 |
C语言程序设计教程(第三版)课后习题6.6 (C语言代码)浏览:584 |