題意:一個數列,每一個元素大小都在1到C之間,求一個最長的子串,知足在這個子串中1到C之間的每一個數字要麼出現0次,要麼出現至少K次。php
題解:\(i\)從1到n枚舉右端點,維護一個\(tree[j]\)表示在\(i\)爲右端點時以\(j\)爲左端點可行的個數(這裏的可行是指對於1到C之間的某一個數是否可行,即\(j\)到\(i\)之間\(X\)的個數是否知足題意,\(X\epsilon(1,C)\))。那麼對於固定的\(i\)顯然當\(tree[j]==C\)時\([j,i]\)是一個可行區間,找出最小的\(j\)便可. 上述操做在線段樹上維護.c++
#include<bits/stdc++.h> using namespace std; const int maxn=100005; int n,c,k,a[100005]; int tree[maxn*4],tag[maxn*4]; inline int ls(int x){ return x<<1; } inline int rs(int x){ return x<<1|1; } void change(int root,int del){ tree[root]+=del; tag[root]+=del; } void pushdown(int root,int l,int r){ if(!tag[root]) return; int mid=(l+r)>>1; change(ls(root),tag[root]); change(rs(root),tag[root]); tag[root]=0; } void update(int root,int l,int r,int il,int ir,int del){ if(r<il||l>ir) return; if(l>=il&&r<=ir) { tree[root] += del; tag[root] += del; return; } pushdown(root,l,r); int mid=(l+r)>>1; update(ls(root),l,mid,il,ir,del); update(rs(root),mid+1,r,il,ir,del); tree[root]=max(tree[ls(root)],tree[rs(root)]); } int ask(int root,int l,int r,int il,int ir){ if(r<il||l>ir) return -1; if(tree[root]<c) return -1; if(l==r) return l; pushdown(root,l,r); int mid=(l+r)>>1; int tmp=ask(ls(root),l,mid,il,ir); if(tmp>0) return tmp; return ask(rs(root),mid+1,r,il,ir); } vector<int> pos[maxn]; int main(){ while(~scanf("%d%d%d",&n,&c,&k)) { for(int i=1;i<=n;i++) scanf("%d",&a[i]); for(int i=1;i<=c;i++) { pos[i].clear(); } if(k==1){ printf("%d\n",n); continue; } int nn=n*4; for(int i=1;i<=nn;i++) tree[i]=0,tag[i]=0; int ans=0; for(int i=1;i<=n;i++){ pos[a[i]].push_back(i); update(1,1,n,i,i,c-1); int siz=pos[a[i]].size(); if(siz>=1){ int l=siz-2,r=siz-1; if(l<0) l=1; else l=pos[a[i]][l]+1; r=pos[a[i]][r]-1; if(l<=r) update(1,1,n,l,r,-1); } if(siz>=k){ int l=siz-k-1,r=siz-k; if(l<0) l=1; else l=pos[a[i]][l]+1; r=pos[a[i]][r]; update(1,1,n,l,r,1); } int tmp=ask(1,1,n,1,i); if(tmp>0) ans=max(ans,i-tmp+1); } printf("%d\n",ans); } return 0; }