赞
踩
题目链接:hdu 6602 博客
思路源自博客:https://blog.nowcoder.net/n/bdacd5d54bdc491ea71ece169c860f39
感觉多校的题目还是比较重思维 而饿我的思维很菜
这题我一开始想了个假算法,因为要个数>=k或者==0,一开始我想着用主席树,先二分一个长度len,再去枚举左端点,假设枚举到了左端点为L 那么右端点R=L+len-1 我们用主席树T[L-1]和T[R]的插值判断是不是这个区间所有的数出现次数为0或者>=k 但是这样复杂度是错的 >=k这个条件看似可以剪掉很多不必要的递归 其实不然 当k==1时 复杂度可能达到O(n)所以爆炸了 大家感兴趣可以看看错误代码
- #include<cstdio>
- #include<cstring>
- #include<algorithm>
- using namespace std;
- const int N = 1e5+10;
- int c[N*30],lson[N*30],rson[N*30],tot,T[N],t[N],a[N];
- int n,g,k,R[N];
- vector<int>v[N];
- void update(int &rt,int las,int l,int r,int pos){
- rt=++tot,c[rt]=c[las]+1;
- int mid = l+r>>1;
- if(l==r) return;
- if(pos<=mid)
- rson[rt]=rson[las],update(lson[rt],lson[las],l,mid,pos);
- else
- lson[rt]=lson[las],update(rson[rt],rson[las],mid+1,r,pos);
- }
- bool query(int ql,int qr,int l,int r){
- if(l==r) return (c[qr]-c[ql])>=k;
- int mid = l+r>>1;
- int lct = c[lson[qr]]-c[lson[ql]],rct = c[rson[qr]]-c[rson[ql]];
- if((lct&&lct<k)||(rct&&rct<k)) return false;
- bool ret = true;
- if(lct>=k) ret&=query(lson[ql],lson[qr],l,mid);
- if(rct>=k) ret&=query(rson[ql],rson[qr],mid+1,r);
- return ret;
- }
- bool check(int len){
- for(int l = 1; l <= n-len+1; l++){
- int r = l+len-1;
- if(r<R[l]) continue;
- if(query(T[l-1],T[r],1,g)) return true;
- }
- return false;
- }
- int main(){
- while(~scanf("%d%d%d",&n,&g,&k)){
- tot=0;
- for(int i = 1; i <= n; i++){
- int x;
- scanf("%d",&x);
- a[i]=x;
- R[i]=n+1;
- }
- for(int i = 1; i <= g; i++) v[i].clear();
- if(k==1){
- printf("%d\n",n);
- continue;
- }
- for(int i = 1; i <= n; i++)
- update(T[i],T[i-1],1,g,a[i]);
- for(int i = n; i >= 1; i--){
- v[a[i]].push_back(i);
- int f = v[a[i]].size();
- if(f>=k){
- R[i]=*(v[a[i]].begin()+f-k);
- }
- }
- //for(int i = 1; i <= n; i++) printf("R[%d]=%d\n",i,R[i]);
- int l=k,r=n,ans;
- while(l<=r){
- int mid = l+r>>1;
- if(check(mid)) l=mid+1,ans=mid;
- else r=mid-1;
- }
- printf("%d\n",ans);
- }
- return 0;
- }
大家对错误解题方法肯定没什么兴趣 接下来是正确方法 :我们枚举每个位置,并把它作为右端点,用线段树动态的维护最左的满足条件的左端点
- 我们定义一个贡献 显然一个值出现0次或者大于等于k次才有贡献 先初始化 对于1到c的每个值x 我们把他们第一次出现的左边的贡献都加1 因为这些位置对于x这个值来说 都是出现0次x 对于x没出现过的情况 整个区间都要更新 为了方便 在存放位置的容器里放两个虚点 分别为0和n+1 中间是x出现的位置 这样可以保证这个初始化是正确的
- 我们从1到n开始遍历 遍历到 i 位置时,因为 i 是线段的右端点 对于 线段 j~i (1<=j<=i) a[ i ] 出现的次数肯定不为0 所以我们又得把 第上述第1点的贡献去掉 (具体更新时 不一定是从1开始 而是从上一次更新的位置)
- 如果 a[ i ] 在1到 i 中出现的次数大于等于k了 假设是now 那么我们需要找到一个位置 这个位置的值为a[i] 并且是 a[i]第now-k次出现 设这个位置为pos1 并且找到a[i] 第now-k+1次出现的位置 设为pos2 显然区间pos1~pos2的贡献+1 因为以这个区间内的位置为左端点和以 i 位置为右端点的线段上 a[ i ] 恰好出现了k次
- 由第1点,我们知道对于1到c的每个值x 我们都只初始化了x出现的最左端的那个位置的左边 然而对于两个相邻的x之间的数 他们也是有1点贡献的 为了不影响当前位置的询问 我们在询问完以后进行更新 例如 数列 1 2 3 5 6 2 9 对于第一个2和第二个2中间的3、5、6 显然对于3、5、6来说3作为左端点时 2出现的次数为0 所以当我们经过第一个2 我们要把区间3~5的贡献加1
- 询问的时候找最左的值为c的位置 我们维护区间的最大值可以轻松完成询问 关键是要理解贡献是啥
- 线段树的用法还是很活的 如何去建立这个模型 还需要多多做题
- #include<bits/stdc++.h>
- using namespace std;
- const int N = 1e5+10;
- #define lson id<<1,l,mid
- #define rson id<<1|1,mid+1,r
- int lazy[N<<2],mx[N<<2],n,c,k,a[N],now[N];
- vector<int>pos[N];
- void pushup(int id){
- mx[id]=max(mx[id<<1],mx[id<<1|1]);
- }
- void pushdown(int id){
- if(lazy[id]){
- mx[id<<1]+=lazy[id];
- mx[id<<1|1]+=lazy[id];
- lazy[id<<1]+=lazy[id];
- lazy[id<<1|1]+=lazy[id];
- lazy[id]=0;
- }
- }
- void build(int id,int l,int r){
- lazy[id]=mx[id]=0;
- if(l==r) return;
- int mid = l+r>>1;
- build(lson);build(rson);
- pushup(id);
- }
- void update(int id,int l,int r,int L,int R,int val){
- if(L>R) return;//注意这里必须要 因为部分区间的LR大小关系不满足
- if(L<=l&&R>=r){
- lazy[id]+=val;
- mx[id]+=val;
- return;
- }
- int mid = l+r>>1;
- pushdown(id);
- if(L<=mid) update(lson,L,R,val);
- if(R>mid) update(rson,L,R,val);
- pushup(id);
- }
- int query(int id,int l,int r){
- if(l==r) return l;
- int ret = -1,mid = l+r>>1;
- pushdown(id);
- if(mx[id<<1]==c) ret=query(lson);
- else if(mx[id<<1|1]==c) ret=query(rson);
- return ret;
- }
- 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 t = a[i];
- update(1,1,n,pos[t][now[t]]+1,pos[t][now[t]+1]-1,-1);
- now[t]++;
- if(now[t]>=k) update(1,1,n,pos[t][now[t]-k]+1,pos[t][now[t]-k+1],1);
- int ql = query(1,1,n);
- if(ql!=-1) ans=max(ans,i-ql+1);
- update(1,1,n,pos[t][now[t]]+1,pos[t][now[t]+1]-1,1);
- }
- printf("%d\n",ans);
- }
- }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。