该题其实就是lrj讲的滑动窗口的一个小小变形, 只不过这个窗口的大小是可变的 。 具体方法其实和例题Shuffle类似,用首尾两个指针维护窗口,用一个数组cnt记录1~k中每个数字在窗口中出现的次数,用一个变量c记录窗口中只出现了一次的数字的个数 。 这样只要c == k 这就是一个满足条件的答案,取最小答案即可 。
由于每个元素都只插入删除一次,所以时间复杂度为O(n)。
该算法还有一个名字叫“取尺法” ,特点是处理一段长度未知的连续区间,通过反复地推进区间的开头和末尾 ,直到得出答案的方法 。
细节参见代码:
#include<bits/stdc++.h> using namespace std; const int maxn = 1000000 + 10; int T,n,m,k,Case = 0,a[maxn],cnt[maxn]; map<int,int> p; vector<int> g[105]; int main() { scanf("%d",&T); while(T--) { scanf("%d%d%d",&n,&m,&k); memset(cnt,0,sizeof(cnt)); a[1] = 1; a[2] = 2; a[3] = 3; if(n>3) for(int i=4;i<=n;i++) { a[i] = (a[i-1]+a[i-2]+a[i-3])%m + 1; } int ans = 2000000000; int rear = 0,last = 1,c = 0; while(true) { if(c == k) { cnt[a[last]] -- ; if(cnt[a[last]] == 0 && a[last] <= k) c--; last++; if(c == k) ans = min(ans , rear - last + 1); } else { rear ++; if(rear > n) break; cnt[a[rear]]++; if(cnt[a[rear]] == 1 && a[rear] <= k) c++; if(c == k) ans = min(ans , rear - last + 1); } } printf("Case %d: ",++Case); if(ans != 2000000000) printf("%d\n",ans); else printf("sequence nai\n"); } return 0; }
版权声明:本文为博主原创文章,未经博主允许不得转载。
时间: 2024-11-29 08:10:32