赞
踩
非常好理解的
i
n
s
e
r
t
insert
insert过程:材料来于
B
l
o
g
2
Blog2
Blog2
假设主席树的上一个版本长这样(框外的数字代表该值域内有几个数,如[1,4] = 3表示有3个数在区间[1,4]内):
现在我们要插入3,先新建一个当前版本的新节点,它指向上个版本根节点的左右子树,同时3在值域[1,4]内,其sum值+1:
然后我们康康它的左右子树值域范围,显然3在右子树的值域内,那我们就新建一个右子树节点,相应节点的sum值也+1:
递归更新直至到达叶子节点,至此一次更新完成:
#include<bits/stdc++.h> using namespace std; int n,m; const int N = 1e5+10; int a[N]; int root[N]; vector<int>vec;//离散 struct Node{ int l,r;//区间 int cnt;//记录l~r中数值出现次数 }tr[N*4+N*17];//NlogN的动态开点 int idx;//记录当前点的id int find(int x){//二分 return lower_bound(vec.begin(),vec.end(),x)-vec.begin(); } int build(int l,int r){ int p=++idx; if(l==r)return p;//叶子 int mid=l+r>>1; tr[p].l=build(l,mid),tr[p].r=build(mid+1,r); return p; } int insert(int p,int l,int r,int x){ int q=++idx;//动态开点 tr[q]=tr[p]; if(l==r){ tr[q].cnt++; return q; } int mid=l+r>>1; if(x<=mid)tr[q].l=insert(tr[p].l,l,mid,x);//只是单点操作 else tr[q].r=insert(tr[p].r,mid+1,r,x); tr[q].cnt=tr[tr[q].l].cnt+tr[tr[q].r].cnt;//类似于pushup操作 return q; } int query(int q,int p,int l,int r,int k){ if(l==r)return r; int cnt=tr[tr[q].l].cnt-tr[tr[p].l].cnt;//类似于前缀和操作,将军的两个阶段之间,早的阶段及前面获得的值不算 int mid=l+r>>1; if(k<=cnt)return query(tr[q].l,tr[p].l,l,mid,k); else return query(tr[q].r,tr[p].r,mid+1,r,k-cnt); } int main(){ cin>>n>>m; for(int i=1;i<=n;i++){cin>>a[i];vec.push_back(a[i]);} sort(vec.begin(),vec.end()); vec.erase(unique(vec.begin(),vec.end()),vec.end());//离散操作,留下有效数值。 root[0]=build(0,vec.size()-1);//建立骨架进行成长,刚开始没有元素,空树 for(int i=1;i<=n;i++){ root[i]=insert(root[i-1],0,vec.size()-1,find(a[i])); } while(m--){ int l,r,k; cin>>l>>r>>k; cout<<vec[query(root[r],root[l-1],0,vec.size()-1,k)]<<endl; } return 0; }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。