当前位置:   article > 正文

HDU-6602 杭电多校 线段树_hdu6602

hdu6602

题目链接: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)所以爆炸了 大家感兴趣可以看看错误代码

  1. #include<cstdio>
  2. #include<cstring>
  3. #include<algorithm>
  4. using namespace std;
  5. const int N = 1e5+10;
  6. int c[N*30],lson[N*30],rson[N*30],tot,T[N],t[N],a[N];
  7. int n,g,k,R[N];
  8. vector<int>v[N];
  9. void update(int &rt,int las,int l,int r,int pos){
  10. rt=++tot,c[rt]=c[las]+1;
  11. int mid = l+r>>1;
  12. if(l==r) return;
  13. if(pos<=mid)
  14. rson[rt]=rson[las],update(lson[rt],lson[las],l,mid,pos);
  15. else
  16. lson[rt]=lson[las],update(rson[rt],rson[las],mid+1,r,pos);
  17. }
  18. bool query(int ql,int qr,int l,int r){
  19. if(l==r) return (c[qr]-c[ql])>=k;
  20. int mid = l+r>>1;
  21. int lct = c[lson[qr]]-c[lson[ql]],rct = c[rson[qr]]-c[rson[ql]];
  22. if((lct&&lct<k)||(rct&&rct<k)) return false;
  23. bool ret = true;
  24. if(lct>=k) ret&=query(lson[ql],lson[qr],l,mid);
  25. if(rct>=k) ret&=query(rson[ql],rson[qr],mid+1,r);
  26. return ret;
  27. }
  28. bool check(int len){
  29. for(int l = 1; l <= n-len+1; l++){
  30. int r = l+len-1;
  31. if(r<R[l]) continue;
  32. if(query(T[l-1],T[r],1,g)) return true;
  33. }
  34. return false;
  35. }
  36. int main(){
  37. while(~scanf("%d%d%d",&n,&g,&k)){
  38. tot=0;
  39. for(int i = 1; i <= n; i++){
  40. int x;
  41. scanf("%d",&x);
  42. a[i]=x;
  43. R[i]=n+1;
  44. }
  45. for(int i = 1; i <= g; i++) v[i].clear();
  46. if(k==1){
  47. printf("%d\n",n);
  48. continue;
  49. }
  50. for(int i = 1; i <= n; i++)
  51. update(T[i],T[i-1],1,g,a[i]);
  52. for(int i = n; i >= 1; i--){
  53. v[a[i]].push_back(i);
  54. int f = v[a[i]].size();
  55. if(f>=k){
  56. R[i]=*(v[a[i]].begin()+f-k);
  57. }
  58. }
  59. //for(int i = 1; i <= n; i++) printf("R[%d]=%d\n",i,R[i]);
  60. int l=k,r=n,ans;
  61. while(l<=r){
  62. int mid = l+r>>1;
  63. if(check(mid)) l=mid+1,ans=mid;
  64. else r=mid-1;
  65. }
  66. printf("%d\n",ans);
  67. }
  68. return 0;
  69. }

大家对错误解题方法肯定没什么兴趣  接下来是正确方法 :我们枚举每个位置,并把它作为右端点,用线段树动态的维护最左的满足条件的左端点

  1. 我们定义一个贡献  显然一个值出现0次或者大于等于k次才有贡献  先初始化  对于1到c的每个值x 我们把他们第一次出现的左边的贡献都加1 因为这些位置对于x这个值来说 都是出现0次x  对于x没出现过的情况 整个区间都要更新 为了方便 在存放位置的容器里放两个虚点 分别为0和n+1 中间是x出现的位置 这样可以保证这个初始化是正确的
  2. 我们从1到n开始遍历  遍历到 i 位置时,因为 i 是线段的右端点  对于 线段 j~i (1<=j<=i) a[ i ] 出现的次数肯定不为0  所以我们又得把 第上述第1点的贡献去掉 (具体更新时 不一定是从1开始 而是从上一次更新的位置)
  3. 如果 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次
  4. 由第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
  5.   询问的时候找最左的值为c的位置  我们维护区间的最大值可以轻松完成询问   关键是要理解贡献是啥
  6. 线段树的用法还是很活的 如何去建立这个模型 还需要多多做题
  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. const int N = 1e5+10;
  4. #define lson id<<1,l,mid
  5. #define rson id<<1|1,mid+1,r
  6. int lazy[N<<2],mx[N<<2],n,c,k,a[N],now[N];
  7. vector<int>pos[N];
  8. void pushup(int id){
  9. mx[id]=max(mx[id<<1],mx[id<<1|1]);
  10. }
  11. void pushdown(int id){
  12. if(lazy[id]){
  13. mx[id<<1]+=lazy[id];
  14. mx[id<<1|1]+=lazy[id];
  15. lazy[id<<1]+=lazy[id];
  16. lazy[id<<1|1]+=lazy[id];
  17. lazy[id]=0;
  18. }
  19. }
  20. void build(int id,int l,int r){
  21. lazy[id]=mx[id]=0;
  22. if(l==r) return;
  23. int mid = l+r>>1;
  24. build(lson);build(rson);
  25. pushup(id);
  26. }
  27. void update(int id,int l,int r,int L,int R,int val){
  28. if(L>R) return;//注意这里必须要 因为部分区间的LR大小关系不满足
  29. if(L<=l&&R>=r){
  30. lazy[id]+=val;
  31. mx[id]+=val;
  32. return;
  33. }
  34. int mid = l+r>>1;
  35. pushdown(id);
  36. if(L<=mid) update(lson,L,R,val);
  37. if(R>mid) update(rson,L,R,val);
  38. pushup(id);
  39. }
  40. int query(int id,int l,int r){
  41. if(l==r) return l;
  42. int ret = -1,mid = l+r>>1;
  43. pushdown(id);
  44. if(mx[id<<1]==c) ret=query(lson);
  45. else if(mx[id<<1|1]==c) ret=query(rson);
  46. return ret;
  47. }
  48. int main(){
  49. while(~scanf("%d%d%d",&n,&c,&k)){
  50. for(int i = 1; i <= c; i++){
  51. pos[i].clear();
  52. pos[i].push_back(0);
  53. }
  54. for(int i = 1; i <= n; i++){
  55. scanf("%d",&a[i]);
  56. pos[a[i]].push_back(i);
  57. }
  58. build(1,1,n);
  59. for(int i = 1; i <= c; i++){
  60. pos[i].push_back(n+1);
  61. update(1,1,n,pos[i][0]+1,pos[i][1]-1,1);
  62. now[i]=0;
  63. }
  64. int ans = 0;
  65. for(int i = 1; i <= n; i++){
  66. int t = a[i];
  67. update(1,1,n,pos[t][now[t]]+1,pos[t][now[t]+1]-1,-1);
  68. now[t]++;
  69. if(now[t]>=k) update(1,1,n,pos[t][now[t]-k]+1,pos[t][now[t]-k+1],1);
  70. int ql = query(1,1,n);
  71. if(ql!=-1) ans=max(ans,i-ql+1);
  72. update(1,1,n,pos[t][now[t]]+1,pos[t][now[t]+1]-1,1);
  73. }
  74. printf("%d\n",ans);
  75. }
  76. }

 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小小林熬夜学编程/article/detail/180227
推荐阅读
相关标签
  

闽ICP备14008679号