题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6602
题解:如果右端点固定,每一种元素可行左端点是两段连续的区间(个数
>
=
k
>= k
>=k 和
=
0
= 0
=0),扫描右端点,用线段树维护区间最大值来维护每一个元素左端点的可行区间,在每种元素左端点的可行区间做区间 + 1。更新答案时查找 值为 C的下标最小值(值为C表示C种元素在这个位置都可行),在线段树上搜索加剪枝,由于每次查找的区间都是[1,i],剪枝后每一次查询的复杂度仍为
n
l
o
g
n
nlogn
nlogn。移动右端点时只会改变新纳入的元素的可行区间,每一次移动右端点维护复杂度为
n
l
o
g
n
nlogn
nlogn。注意每种元素不要重复维护已经维护过的区间。
#include<bits/stdc++.h>
using namespace std;
#define lson rt << 1,l,mid
#define rson rt << 1 | 1,mid + 1,r
const int maxn = 1e5 + 10;
int val[maxn << 2],laz[maxn << 2],p[maxn << 2];
int n,c,k;
vector<int> g[maxn];
void up(int rt) {
val[rt] = max(val[rt << 1],val[rt << 1 | 1]);
}
void down(int rt) {
if(!laz[rt]) return;
val[rt << 1] += laz[rt];
laz[rt << 1] += laz[rt];
val[rt << 1 | 1] += laz[rt];
laz[rt << 1 | 1] += laz[rt];
laz[rt] = 0;
}
void build(int rt,int l,int r) {
val[rt] = laz[rt] = 0;
if(l == r) {
p[l] = rt;
return ;
}
int mid = l + r >> 1;
build(lson);build(rson);
}
void upd(int L,int R,int v,int rt,int l,int r) {
if(L > R) return ;
if(L <= l && r <= R) {
laz[rt] += v;
val[rt] += v;
return ;
}
down(rt);
int mid = l + r >> 1;
if(L <= mid) upd(L,R,v,lson);
if(mid + 1 <= R) upd(L,R,v,rson);
up(rt);
}
int qry(int L,int R,int v,int rt,int l,int r) {
if(L > R) return 0;
if(val[rt] != c || (l < l && r > R)) return 0;
if(l == r) return l;
int mid = l + r >> 1;
down(rt);
if(L <= mid) {
int t = qry(L,R,v,lson);
if(t) return t;
}
if(mid + 1 <= R) return qry(L,R,v,rson);
}
int main() {
//freopen("in.txt","r",stdin);
while(~scanf("%d%d%d",&n,&c,&k)) {
int x;
build(1,1,n);
for(int i = 1; i <= c; i++) {
g[i].clear();
g[i].push_back(0);
}
int ans = 0;
for(int i = 1; i <= n; i++) {
scanf("%d",&x);
upd(i,i,c - 1,1,1,n);
upd(g[x].back() + 1,i - 1,-1,1,1,n);
g[x].push_back(i);
int p = (int)g[x].size() - k - 1;
if(p >= 0)
upd(g[x][p] + 1,g[x][p + 1],1,1,1,n);
int t = qry(1,i,c,1,1,n);
if(t)
ans = max(ans,i - t + 1);
}
printf("%d\n",ans);
}
return 0;
}