题意:一个数列,每一个元素大小都在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; }