当前位置:   article > 正文

权值线段树与主席树(初步)

权值线段树

1.首先,给你一颗值为横坐标的线段树,每个节点上存着该值出现了多少次,这样的一颗线段树你会求区间k大值吧.二分即可.

2.然后,假设区间是数组arr[n],区间长度是n,那么给你n颗线段树,第i颗线段树是第i-1颗线段树插入arr[i]得到.

3.如果你有了这n颗线段树,想求区间[l,r]中的第k大值,那么你需要在第r颗和第l-1颗线段树的差线段树上作二分,可以求得区间第k大值.

4.差线段树很好理解,比如你有一个部分和数组sum,sum[r]-sum[l-1]就是部分和的差,代表区间[l,r]的和,差线段树同理.

5.还有一个问题需要解决,那就是空间问题。显而易见的是,如果每输入一个数就重新构造一棵权值线段树,必然会导致空间不够用:一棵线段树的空间就是n*4,那么一共的空间开销就是n*n*4,显然是会MLE的。那么这个问题怎么解决呢?可以发现每更新一个点,就会从它开始把它的所有祖先都更新一次,而其他的点都没有被改变,即:每次改变的结点只有logn个。这样,我们每次输入一个数,只需要多开logn个空间,那么实际的空间开销只有n*(4+logn),满足了空间要求。
 

可持久化线段树的核心思想是既然变化很小就只记录变化(用数组模拟链表,串其各个时期的线段树).
用它求区间k大值的核心思想是部分和思想,也就是差线段树
(或者可以说是通过线段树使两个前缀和做差)

来自 知乎Comzyh

额我向来是电科大的粉丝,附上卿学姐的B站视频和卿学姐的代码

其实可以把主席树想象成一颗链树,在链树上的二分查找,就有点像链表那样,通过链表的方式实现不同时期的主席树之间的差值查询。

总之这种数据结构题在区域赛上是为了争银屁屁用的,有点难,Mandy.H.Y殇雪还有Stupid_Turtle这三位大佬写得很nice。

并附上poj 2104 简单的求区间第K大个数的代码。

  1. #include<iostream>
  2. #include<algorithm>
  3. #include<vector>
  4. #include<string.h>
  5. using namespace std;
  6. typedef long long ll;
  7. const int maxn=0x3f3f3f3f;
  8. const int ans=1e5+7;
  9. int n,m,cnt,root[ans],a[ans],x,y,k;
  10. struct node{
  11. int l;
  12. int r;
  13. int sum;
  14. }T[ans*40];
  15. vector<int>v;
  16. int getid(int x){return lower_bound(v.begin(),v.end(),x)-v.begin()+1;}
  17. void update(int l,int r,int &x,int y,int pos){
  18. T[++cnt]=T[y],T[cnt].sum++,x=cnt;
  19. if(l==r) return;
  20. int mid=(l+r)/2;
  21. if(mid>=pos) update(l,mid,T[x].l,T[y].l,pos);
  22. else update(mid+1,r,T[x].r,T[y].r,pos);
  23. }
  24. int query(int l,int r,int x,int y,int k){
  25. if(l==r) return l;
  26. int mid=(l+r)/2;
  27. int sum=T[T[y].l].sum-T[T[x].l].sum;
  28. if(sum>=k) return query(l,mid,T[x].l,T[y].l,k);
  29. else return query(mid+1,r,T[x].r,T[y].r,k-sum);
  30. }
  31. int main(){
  32. scanf("%d%d",&n,&m);
  33. for(int i=1;i<=n;i++)scanf("%d",&a[i]),v.push_back(a[i]);
  34. sort(v.begin(),v.end());//离散化
  35. v.erase(unique(v.begin(),v.end()),v.end());
  36. //离线处理
  37. for(int i=1;i<=n;i++) update(1,n,root[i],root[i-1],getid(a[i]));
  38. //离线处理后查询
  39. for(int i=1;i<=m;i++){
  40. scanf("%d%d%d",&x,&y,&k);
  41. printf("%d\n",v[query(1,n,root[x-1],root[y],k)-1]);
  42. }
  43. return 0;
  44. }

 

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

闽ICP备14008679号