调查
调查( 单调队列\(\star \))
- 时限:\(1s\) 内存:\(256M\)
Descrption
- \(Ezio\) 现在有一份调查列表,上面有 \(n\) 个人,每个人有一个编号,编号在 \(1\) 到 \(m\) 之间,编号记录的是这个人与哪一条情报有关,\(Ezio\) 现在要调查 \(1\) 到 \(m\) 所有的情报,每条情报只需要调查与该条情报有关的一个人即可,由于时间关系,\(Ezio\) 想要调查连续的一段人,并且调查的人数尽量少。
Input
- 共两行,第一行两个数 \(n,m\) 。
- 第二行 \(n\) 个数 第 \(i\) 个数是,第 \(i\) 个人的编号。每两个数中间有一个空格隔开,结尾无空格。
Output
- 一个数,满足条件的最少人数。对于无解的情况,输出 \(0\)。
Sample Input
7 6
6 1 2 4 4 5 3
Sample Output
7
Hint
- \(n<=5000000, m<=n\)
- 来源:\(cogs2410\)
分析
- 典型的单调队列,和 \(luoguP1638\) 类似,用一个队列来维护,扫描整个序列,并记录元素出现的个数和新元素的个数,当队首元素的个数大于 \(1\) 时,说明后面有替代队首的相同元素,所以可以出队,当新元素个数等于 \(m\) 时,说明队列里 \(1\sim m\) 都有了,维护一个最小值即可。
Code
#include <bits/stdc++.h>
typedef long long LL;
const int maxn= 5e6+5,Inf=0x3f3f3f3f;
int cnt[maxn],q[maxn],tail=0,head=1,tot,ans=Inf;
void Solve(){
int n,m;scanf("%d%d",&n,&m);
for(int i=1;i<=n;++i){
int x;scanf("%d",&x);
q[++tail]=x;
if(!cnt[x])tot++;//x没有出现过
cnt[x]++;//记录x出现的次数
while(cnt[q[head]]>1)//队首元素个数不止一个
cnt[q[head]]--,head++;
if(tot==m)ans=std::min(ans,tail-head+1);
}
if(ans==Inf)
printf("0\n");
else
printf("%d\n",ans);
}
int main(){
Solve();
return 0;
}
hzoi