题目链接 http://poj.org/problem?id=2104
动态找区间第k大
在不知道主席树前想过啥。。。每给一个区间给每个区间排序。。。知道主席树后感觉好神奇。维护从小到大数的个数的前缀和,还有很多历史版本的树,通过差分来知道某个区间的前缀和。
主席树巧妙利用了之前建过的树,节约了空间
主席树
1.先建一最初的树(值空)
2.区间后移一格,加入一个结点,实际修改了logn个结点,不动的结点就赋值为之前建的结点
3.继续后移,重复2,把整个数组每个数建一颗树。这样有一个froot[] 存每个值对应的新建的树的根节点
板子抄的kuangbin 做了修改和注释,非递归建树,改为正着建树。
学习链接:https://blog.csdn.net/pengwill97/article/details/80920143
1 #include<iostream> 2 #include<cstdio> 3 #include<algorithm> 4 using namespace std; 5 typedef long long ll; 6 int const maxn=1e5+10; 7 int m,n,q,a[maxn],b[maxn],tot,froot[maxn],c[maxn*40],lson[maxn*40],rson[maxn*40];//主席树的空间开n的40倍一般够了 8 void init_hash(int x[]){//数值离散 9 for(int i=1;i<=n;i++)b[i]=a[i]; 10 sort(b+1,b+n+1); 11 m=unique(b+1,b+n+1)-b-1; 12 } 13 int get_hash(int x){ 14 return lower_bound(b+1,b+m+1,x)-b; 15 } 16 17 int update(int root,int pos,int val){//加入一个新的值后新的树,root是前一棵树 18 int newroot=tot++,tmp=newroot; 19 c[newroot]=c[root]+val;//新树的结点值=原先树的值+新加的 20 int l=1,r=m; 21 while(l<r){ 22 int mid=(l+r)>>1; 23 if(pos<=mid){//看修改的值位于哪颗子树上 24 lson[newroot]=tot++;rson[newroot]=rson[root];//如果位于左子树上,则右子树的值可以利用原先的树 25 newroot = lson[newroot];root=lson[root]; 26 r=mid; 27 } 28 else { 29 rson[newroot]=tot++;lson[newroot]=lson[root]; 30 newroot=rson[newroot];root=rson[root]; 31 l=mid+1; 32 } 33 c[newroot]=c[root]+val; 34 } 35 return tmp;//新的树的根节点 36 } 37 int query(int lr,int rr,int k){ 38 int l=1,r=m,mid; 39 while(l<r){ 40 mid=(l+r)>>1; 41 if(c[lson[rr]]-c[lson[lr]]>=k){//求l-r区间,用r树-(l-1)树 ,如果左子树有k个了,同时向左子树跳 42 r=mid; 43 lr=lson[lr],rr=lson[rr]; 44 } 45 else{//右子树有j(<k)个,向右子树跳,但应该找k-j 大 46 l=mid+1;k-=c[lson[rr]]-c[lson[lr]]; 47 lr=rson[lr],rr=rson[rr]; 48 } 49 } 50 return l; 51 } 52 int build(int l,int r){//初始建树 53 int root=tot++; 54 c[tot]=0; 55 if(l!=r){ 56 int mid=(l+r)>>1; 57 lson[root]=build(l,mid); 58 rson[root]=build(mid+1,r); 59 } 60 return root; 61 } 62 inline int get_num(){//此题有负数!absolute value 63 char ch; 64 bool flag=false; 65 int num=0; 66 ch=getchar(); 67 while(ch<'0'||ch>'9'){if(ch=='-')flag=true;ch=getchar();} 68 while(ch>='0'&&ch<='9'){num=(num<<3)+(num<<1)+ch-'0';ch=getchar();} 69 if(flag)return -1*num; 70 return num; 71 } 72 int main(){ 73 scanf("%d%d",&n,&q); 74 tot=0; 75 for(int i=1;i<=n;i++)a[i]=get_num(); 76 77 init_hash(a); 78 froot[0]= build(1,m); 79 for(int i=1;i<=n;i++){ 80 int pos=get_hash(a[i]); 81 froot[i]=update(froot[i-1],pos,1); 82 } 83 while(q--){ 84 int l,r,k; 85 l=get_num(),r=get_num(),k=get_num(); 86 printf("%d ",b[query(froot[l-1],froot[r],k)]); 87 } 88 return 0; 89 }