http://acm.hdu.edu.cn/showproblem.php?pid=6602

题意:给出一个1e5规模数列,数字范围也在1e5内,给出K,求最长子序列,使得序列中出现的任一数字都至少出现K次。

考虑枚举右端点,尺取法不好做,因为左端点没有简单的找法,,我们这样拆分问题:首先考虑每个数字的合法左端点的取法,易知对于数字a,若i出现不足K次,则左端点只能在最后一次a出现之后,即(最后一次a出现的位置,i];如果a已经出现了K次,则还可以在[1,往前数第K次的位置)取到。于是我们考虑到,可以沿着数列a[i]更新区间状态,对于每个a[i],根据a[i]是否已出现K次有两种更新左端点合理区间的操作。所以可以用线段树,使得对于每个数来说合理的左端点区间取值为1,不合理为0。最后query时,若某个点上值为c(数值上限),则说明该区间可取,因为每个数字在这个区间上都是合理的(要么不出现,要么已出现K次)。

 

#include<iostream>
#include<cstdio>
#include<vector>
using namespace std;

#define ls (rt<<1)
#define rs (rt<<1|1)
const int maxn=100010;
int n,c,k;
int a[maxn];
vector<int> pos[maxn];
struct Node{
    int dat,lazy;
    //dat的值为该段的合法点数量,需为c
}tr[maxn<<2];

int now[maxn];//表示某个数值的当前坐标

void pushup(int rt){
    tr[rt].dat=max(tr[ls].dat,tr[rs].dat);
    //有一个合法则该段合法
}
void pushdown(int rt){
    if(tr[rt].lazy){
        tr[ls].dat+=tr[rt].lazy;
        tr[rs].dat+=tr[rt].lazy;
        tr[ls].lazy+=tr[rt].lazy;
        tr[rs].lazy+=tr[rt].lazy;
        tr[rt].lazy=0;
    }
}
void build(int rt,int l,int r){
    tr[rt].lazy=0;

    if(l==r){
        tr[rt].dat=0;
        return;
    }
    int mid=(l+r)>>1;
    build(ls,l,mid);
    build(rs,mid+1,r);
    pushup(rt);
}
void update(int rt,int l,int r,int L,int R,int val){
    if(L>R)
        return;
    if(L<=l&&r<=R){
        tr[rt].dat+=val;
        tr[rt].lazy+=val;
        return;
    }
    pushdown(rt);   //要对该区间继续向下更新了,先把lazy标记处理了
    int mid=(l+r)>>1;
    if(L<=mid){
        update(ls,l,mid,L,R,val);
    }
    if(R>mid){
        update(rs,mid+1,r,L,R,val);
    }
    pushup(rt);
}
int query(int rt,int l,int r){
    if(l==r){
        return l;
    }
    int res=-1;
    int mid=(l+r)>>1;
    pushdown(rt);
    if(tr[ls].dat==c)
        res=query(ls,l,mid);
    else if(tr[rs].dat==c)
        res=query(rs,mid+1,r);
    return res;
}

int main(){
    while(~scanf("%d%d%d",&n,&c,&k)){
        for(int i=1;i<=c;i++){
            pos[i].clear();
            pos[i].push_back(0);
        }
        for(int i=1;i<=n;i++){
            scanf("%d",a+i);
            pos[a[i]].push_back(i);
        }
        build(1,1,n);
        for(int i=1;i<=c;i++){
            pos[i].push_back(n+1);
            update(1,1,n,pos[i][0]+1,pos[i][1]-1,1);
            now[i]=0;
        }
        int ans=0;
        for(int i=1;i<=n;i++){
            int res=0;
            int t=a[i];
            update(1,1,n,pos[t][now[t]]+1,pos[t][now[t]+1]-1,-1);
//            if(now[t]>=k)
//                update(1,1,n,1,pos[t][now[t]-k+1],-1);
            now[t]++;   //更新当前值的个数
            update(1,1,n,pos[t][now[t]]+1,pos[t][now[t]+1]-1,1);
            if(now[t]>=k)
                update(1,1,n,pos[t][now[t]-k]+1,pos[t][now[t]-k+1],1);

            res=query(1,1,n);
            if(res!=-1)
                ans=max(ans,i-res+1);
        }
        printf("%d\n",ans);
    }
    return 0;
}