当前位置:   article > 正文

杭电2019多校第二场 HDU-6602 Longest Subarray(线段树+lazy标记)_hdu 6602 longest subarray

hdu 6602 longest subarray

链接: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 的下标即可。

详情请看图片和注释:

 

 

  1. #include <bits/stdc++.h>
  2. using namespace std;
  3. const int N = 1e5+10;
  4. int a[N],n,c,k,ans,ll;
  5. vector<int> ve[N];
  6. struct node
  7. {
  8. int l,r,val,lazy;
  9. //val:表示在[l,r]区间内,有多少个数符合要求
  10. //lazy:区间更新,lazy标记,记下变化量
  11. }tree[N<<2];
  12. void Init()
  13. {
  14. for(int i=1;i<=c;i++)
  15. ve[i].clear();
  16. return ;
  17. }
  18. void pushup(int cur)
  19. {
  20. tree[cur].val=max(tree[cur<<1].val,tree[cur<<1|1].val);
  21. return ;
  22. }
  23. void build(int l,int r,int cur)
  24. {
  25. tree[cur].l=l;
  26. tree[cur].r=r;
  27. tree[cur].lazy=tree[cur].val=0;
  28. if(l==r)
  29. {
  30. //如果是第一个,那么个数肯定是c-1
  31. //因为下面枚举右端点是从2开始的,这里特别处理
  32. if(l==1)
  33. tree[cur].val=c-1;
  34. return;
  35. }
  36. int m=(l+r)>>1;
  37. build(l,m,cur<<1);
  38. build(m+1,r,cur<<1|1);
  39. pushup(cur);
  40. return;
  41. }
  42. //有lazy标记,要向下更新
  43. void pushdown(int cur)
  44. {
  45. if(tree[cur].lazy)
  46. {
  47. tree[cur<<1].lazy+=tree[cur].lazy;
  48. tree[cur<<1].val+=tree[cur].lazy;
  49. tree[cur<<1|1].lazy+=tree[cur].lazy;
  50. tree[cur<<1|1].val+=tree[cur].lazy;
  51. tree[cur].lazy=0;
  52. }
  53. return ;
  54. }
  55. void update(int l,int r,int cur,int val)
  56. {
  57. if(l<=tree[cur].l&&tree[cur].r<=r)
  58. {
  59. tree[cur].val+=val;
  60. tree[cur].lazy+=val;
  61. return ;
  62. }
  63. pushdown(cur);
  64. if(l<=tree[cur<<1].r) update(l,r,cur<<1,val);
  65. if(r>=tree[cur<<1|1].l) update(l,r,cur<<1|1,val);
  66. pushup(cur);
  67. return ;
  68. }
  69. void query(int l,int r,int cur)
  70. {
  71. if(tree[cur].l==tree[cur].r)
  72. {
  73. //cout<<ll<<endl;
  74. ll=tree[cur].l;
  75. return ;
  76. }
  77. //lazy标记,询问时也要pushdown
  78. pushdown(cur);
  79. //因为要找最小的可行左端点,左子树如果符合条件,
  80. //直接查找左子树,因为左子树的l小
  81. if(tree[cur<<1].val==c) query(l,r,cur<<1);
  82. //否则,就要判断右子树符不符合条件和是不是包含在询问的[l,r]区间
  83. //符合并包含,就递归的询问右子树
  84. else if(tree[cur<<1|1].l<=r&&tree[cur<<1|1].val==c) query(l,r,cur<<1|1);
  85. return;
  86. }
  87. int main(void)
  88. {
  89. int pos,l0,r0,lk,rk,num;
  90. while(~scanf("%d%d%d",&n,&c,&k))
  91. {
  92. Init();
  93. ans=0;
  94. for(int i=1;i<=n;i++)
  95. {
  96. scanf("%d",&a[i]);
  97. ve[a[i]].push_back(i);
  98. }
  99. build(1,n,1);
  100. //如果k=1,那么要把[1,1]这个区间的val+1
  101. //因为建树的时候是c-1,现在k=1,那么a[1]的个数
  102. //在[1,1]这个区间内为1(==k)符合要求。
  103. if(k==1)
  104. {
  105. update(1,1,1,1);
  106. ans=1;
  107. }
  108. for(int i=2;i<=n;i++)
  109. {
  110. //在[i,i]这个区间内,符合要求的数肯定是>=c-1个
  111. //因为这个区间内肯定有a[i]这个数,剩下的c-1个数,数目都为0,符合要求
  112. //如果给出的k==1,那么符合要求的数就是c个,在下面会更新到。
  113. update(i,i,1,c-1);
  114. //计算i位置上的a[i]的下标,也就是他是第num=pos+1个a[i],因为下标是从0开始的,所以+1。
  115. pos=lower_bound(ve[a[i]].begin(),ve[a[i]].end(),i)-ve[a[i]].begin();
  116. num=pos+1;
  117. //首先,把[1,i-1]这个区间内,a[i]个数为0的区间的val-1。
  118. //[1,i]区间内最右边的a[i]的位置就是i。
  119. //如果i位置上的a[i]是第一个a[i],那么区间左端点就是1,否则就是上一个a[i]的位置+1。
  120. if(pos==0) l0=1;
  121. else l0=ve[a[i]][pos-1]+1;
  122. //右端点肯定是i-1
  123. r0=i-1;
  124. //把这个区间的val-1,即把原先a[i]个数为0的区间减去。
  125. update(l0,r0,1,-1);
  126. //如果i位置上这个a[i],是第大于等于k个a[i]
  127. //那么现在要把右往左数,第k-1个a[i]和第k个a[i]这个区间的val+1,
  128. //即把原先a[i]个数为k-1的区间加上。
  129. if(num>=k)
  130. {
  131. //处理num==k的情况
  132. if(pos-k<0) lk=1;
  133. else lk=ve[a[i]][pos-k]+1;
  134. rk=ve[a[i]][pos-k+1];
  135. update(lk,rk,1,1);
  136. }
  137. ll=0;
  138. query(1,i,1);
  139. if(ll!=0) ans=max(ans,i-ll+1);
  140. }
  141. printf("%d\n",ans);
  142. }
  143. return 0;
  144. }

 

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

闽ICP备14008679号