调查

调查( 单调队列\(\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;
}
posted @ 2020-07-25 14:13  ♞老姚♘  阅读(202)  评论(0编辑  收藏  举报