赞
踩
链接:http://acm.hdu.edu.cn/showproblem.php?pid=6602
题意:多组样例。先给出n,k,c。接下来一行n个数(1<=a[i]<=c)。问最大的区间长度,对于 (1<=i<=c),这c个数,要么其个数为0,要么其个数>=k。
思路:
官方题解:如果右端点固定,对于每种元素,可行的左端点下标是两段连续的区间。对于每种元素,将它的可行左端点区间在线段树中加一。当右端点右移的时候,维护C 种元素的可行左端点。查询时只需要询问线段树中最小的、值为C 的下标即可。
详情请看图片和注释:
- #include <bits/stdc++.h>
- using namespace std;
- const int N = 1e5+10;
- int a[N],n,c,k,ans,ll;
- vector<int> ve[N];
- struct node
- {
- int l,r,val,lazy;
- //val:表示在[l,r]区间内,有多少个数符合要求
- //lazy:区间更新,lazy标记,记下变化量
- }tree[N<<2];
- void Init()
- {
- for(int i=1;i<=c;i++)
- ve[i].clear();
- return ;
- }
- void pushup(int cur)
- {
- tree[cur].val=max(tree[cur<<1].val,tree[cur<<1|1].val);
- return ;
- }
- void build(int l,int r,int cur)
- {
- tree[cur].l=l;
- tree[cur].r=r;
- tree[cur].lazy=tree[cur].val=0;
- if(l==r)
- {
- //如果是第一个,那么个数肯定是c-1
- //因为下面枚举右端点是从2开始的,这里特别处理
- if(l==1)
- tree[cur].val=c-1;
- return;
- }
- int m=(l+r)>>1;
- build(l,m,cur<<1);
- build(m+1,r,cur<<1|1);
- pushup(cur);
- return;
- }
-
- //有lazy标记,要向下更新
- void pushdown(int cur)
- {
- if(tree[cur].lazy)
- {
- tree[cur<<1].lazy+=tree[cur].lazy;
- tree[cur<<1].val+=tree[cur].lazy;
-
- tree[cur<<1|1].lazy+=tree[cur].lazy;
- tree[cur<<1|1].val+=tree[cur].lazy;
-
- tree[cur].lazy=0;
- }
- return ;
- }
- void update(int l,int r,int cur,int val)
- {
- if(l<=tree[cur].l&&tree[cur].r<=r)
- {
- tree[cur].val+=val;
- tree[cur].lazy+=val;
- return ;
- }
- pushdown(cur);
- if(l<=tree[cur<<1].r) update(l,r,cur<<1,val);
- if(r>=tree[cur<<1|1].l) update(l,r,cur<<1|1,val);
- pushup(cur);
- return ;
- }
- void query(int l,int r,int cur)
- {
-
- if(tree[cur].l==tree[cur].r)
- {
- //cout<<ll<<endl;
- ll=tree[cur].l;
- return ;
- }
- //lazy标记,询问时也要pushdown
- pushdown(cur);
- //因为要找最小的可行左端点,左子树如果符合条件,
- //直接查找左子树,因为左子树的l小
- if(tree[cur<<1].val==c) query(l,r,cur<<1);
- //否则,就要判断右子树符不符合条件和是不是包含在询问的[l,r]区间
- //符合并包含,就递归的询问右子树
- else if(tree[cur<<1|1].l<=r&&tree[cur<<1|1].val==c) query(l,r,cur<<1|1);
- return;
- }
-
- int main(void)
- {
- int pos,l0,r0,lk,rk,num;
- while(~scanf("%d%d%d",&n,&c,&k))
- {
- Init();
- ans=0;
- for(int i=1;i<=n;i++)
- {
- scanf("%d",&a[i]);
- ve[a[i]].push_back(i);
- }
- build(1,n,1);
- //如果k=1,那么要把[1,1]这个区间的val+1
- //因为建树的时候是c-1,现在k=1,那么a[1]的个数
- //在[1,1]这个区间内为1(==k)符合要求。
- if(k==1)
- {
- update(1,1,1,1);
- ans=1;
- }
- for(int i=2;i<=n;i++)
- {
- //在[i,i]这个区间内,符合要求的数肯定是>=c-1个
- //因为这个区间内肯定有a[i]这个数,剩下的c-1个数,数目都为0,符合要求
- //如果给出的k==1,那么符合要求的数就是c个,在下面会更新到。
- update(i,i,1,c-1);
-
- //计算i位置上的a[i]的下标,也就是他是第num=pos+1个a[i],因为下标是从0开始的,所以+1。
- pos=lower_bound(ve[a[i]].begin(),ve[a[i]].end(),i)-ve[a[i]].begin();
- num=pos+1;
-
- //首先,把[1,i-1]这个区间内,a[i]个数为0的区间的val-1。
- //[1,i]区间内最右边的a[i]的位置就是i。
- //如果i位置上的a[i]是第一个a[i],那么区间左端点就是1,否则就是上一个a[i]的位置+1。
- if(pos==0) l0=1;
- else l0=ve[a[i]][pos-1]+1;
- //右端点肯定是i-1
- r0=i-1;
- //把这个区间的val-1,即把原先a[i]个数为0的区间减去。
- update(l0,r0,1,-1);
-
- //如果i位置上这个a[i],是第大于等于k个a[i]
- //那么现在要把右往左数,第k-1个a[i]和第k个a[i]这个区间的val+1,
- //即把原先a[i]个数为k-1的区间加上。
- if(num>=k)
- {
- //处理num==k的情况
- if(pos-k<0) lk=1;
- else lk=ve[a[i]][pos-k]+1;
-
- rk=ve[a[i]][pos-k+1];
- update(lk,rk,1,1);
- }
- ll=0;
- query(1,i,1);
- if(ll!=0) ans=max(ans,i-ll+1);
- }
- printf("%d\n",ans);
- }
-
-
- return 0;
- }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。