主席树经典题目 区间k大值 poj2104

来源:互联网 发布:环评做题软件 编辑:程序博客网 时间:2024/05/01 11:31

年前就开始写的一个题。。。一直没调。。

中间贡献无数wa原因是忘了函数式的数据结构要开30倍空间。。。。


至今对主席树的理解还是不算很深

主席树大概就是对每个前缀或者后缀都单独的维护一颗线段树,然后由于主席树的可加减性,可以减出来区间的情况


用丽杰姐姐的话来说就是划分树已经成为时代的眼泪。。。。


后来稍微懂了。。。

大概就是首先闹一个 rank

这样我们就知道每个数在现在区间的排名了,相当于是一个离散的过程,当然有重复就麻烦了

然后我们每个节点保存的 就是一个数字而已,大概意思是这段范围内一共出现了几次

但是我们并不保留具体的值


因为主席树是可以减的,所以一直减,一直到叶节点,就显而易见了。。。

这是主席树的精髓。。

#include<iostream>#include<algorithm>#include<cstring>#include<cstdio>#define MAX 100010using namespace std;int t[MAX*30];int n,c[MAX],a[MAX],child[MAX*30][2],size,tree[MAX*30];int tot=0,m;bool cmp(int a1,int a2){return a1<a2;}void before(){for(int i=1;i<=n;i++)c[i]=a[i];sort(c+1,c+1+n,cmp);size=unique(c+1,c+1+n)-(c+1);}int build(int la,int ra){int root=tot++;tree[root]=0;if(la!=ra){int mid=(la+ra)/2; child[root][0]=build(la,mid); child[root][1]=build(mid+1,ra);}return root;}int find(int x){return lower_bound(c+1,c+1+size,x)-c;}int update(int root,int pos,int value){int newnode=tot++,tmp=newnode;tree[newnode]=tree[root]+value;int l=1,r=size;while(l<r){int mid=(l+r)>>1;if(pos<=mid){child[newnode][0]=tot++;child[newnode][1]=child[root][1];newnode=child[newnode][0];root=child[root][0];r=mid;}else{child[newnode][1]=tot++;child[newnode][0]=child[root][0];newnode=child[newnode][1];root=child[root][1];l=mid+1;}tree[newnode]=tree[root]+value;}return tmp;}int ask(int l,int r,int k){int L=1,R=size;while(L<R){int mid=(L+R)>>1;if(tree[child[l][0]]-tree[child[r][0]]>=k){R=mid;l=child[l][0];r=child[r][0];}else{L=mid+1;k-=tree[child[l][0]]-tree[child[r][0]];l=child[l][1];r=child[r][1];}}return L;}int main(){while(scanf("%d%d",&n,&m)==2){for(int i=1;i<=n;i++)scanf("%d",&a[i]);before();tot=0;t[n+1]=build(1,size);for(int i=n;i>=1;i--){int pos=find(a[i]);t[i]=update(t[i+1],pos,1);}while(m--){int l,r,k;scanf("%d %d %d",&l,&r,&k);printf("%d\n",c[ask(t[l],t[r+1],k)]);}}return 0;}

1 0
原创粉丝点击