-
题目链接:
-
题目大意:
给定一个(N,M,K),构造这样的数列:
(x[1]=1,x[2]=2,x[3]=3)
(x[i]=(x[i-1]+x[i-2]+x[i-3])mod M+1(N>=i>=4))然后问你是否存在一个在(x[1])到(x[n])中的区间,使得([1,K])所有元素在其中至少出现过一次。
若存在,输出这个区间最短长度;否则输出("sequence) (nai")
有T组数据
-
分析:
我们可以想到用滑动窗口的改进做法。
搞一个左指针(l),右指针(r),和一个(cnt)数组记录每个元素相对应在([l,r])中的出现次数。
然后不断尝试右移(r),同时将(cnt[x[r]]++),如果(cnt[x[r]]==1),就让(done++),表示多了一个新元素出现在区间内,如果(done==k),说明所有元素出现过一次,这时尝试右移左指针,用上面类似的操作,只不过判定有所区别,直到(done!=k)说明不满足性质,记录下此时(r-l)长度,再重复右移(r)的操作
其实如果你对莫队有了解的话这些应该很容易理解
-
注意:
庆幸的是由于(mod M)的限制,数列中的数都很小,可以直接记录,不必用map或离散化
-
代码:
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <cctype>
#define inf 0xfffffff
using namespace std;
const int maxn=1000009;
int t,n,m,k;
int f[maxn];
int cnt[1005];
int main(){
scanf("%d",&t);
for(register int ite=1;ite<=t;ite++){
memset(cnt,0,sizeof(cnt));
scanf("%d %d %d",&n,&m,&k);
f[1]=1,f[2]=2,f[3]=3;
for(register int i=4;i<=n;i++)f[i]=(f[i-1]+f[i-2]+f[i-3])%m+1;
int l=1,r=1,done=0,ans=inf;
while(r<n){
int g=f[r++];
if(g>=1&&g<=k)cnt[g]++;
if(cnt[g]==1)done++;
while(done==k&&l<r){
ans=min(ans,r-l);
g=f[l++];
cnt[g]--;
if(cnt[g]==0)done--;
}
}
printf("Case %d: ",ite);
if(ans==inf)puts("sequence nai");
else printf("%d
",ans);
}
return 0;
}