@Bei-S
2019-01-14T00:15:02.000000Z
字数 1469
阅读 1389
数据结构
起风了,唯有努力生存。
随风逝,不带一丝伤痕。
有些时候我们会遇到一些奇奇怪怪的题目,例如需要你记录下一个数组的历史版本。
这时候我们怎么办呢?暴力所有开m个线段树/数组?的空间估计多半吃不消
这时候我们就要引入一个神奇的数据解构 ——— 可持久化线段树(主席树)。
主席树主要可以用于处理区间第K大之类的问题。
而主席树的实质呢就是对每个前缀([1,1],[1,2],[1,3]...)建立一颗权值线段树,每次更改时如果左(右)儿子没变,我们直接指向他上一颗树的儿子即可,同时我们记录每颗权值线段树的根节点和左右儿子。这样每次我们最多更改个节点,空间降为
我们可以看出这个主席树是具有可减性的。例如我们要求区间[l,r]的第K大时,我们只需用第r颗线段树减去第l-1颗线段树即可。
当然代码中我们肯定不是直接减啦,我们只需每次往下传递节点,统计答案时直接用即可,这里r和l-1指的是这两颗权值线段树上的节点,不一定是L-1,R哦。
那么可持久化数组就可以用主席树轻松实现了,因为每次相当于更改一个节点,即单点修改。
基于此,我们就可以愉快地实现可持久化冰碴鸡了(@菜XX)
至于可持久化平衡树么。。。等我学了FHQ Treap再说吧
Code:
#include<bits/stdc++.h>using namespace std;#define mid ((l+r)>>1)const int N=2e5+7;int n,m,q,tot;int a[N],b[N];int rt[N],sum[N*20],ls[N*20],rs[N*20];inline int build(int l,int r){int s=++tot;if(l<r){ls[s]=build(l,mid);rs[s]=build(mid+1,r);}return s;}inline int update(int pre,int l,int r,int x){int s=++tot;ls[s]=ls[pre];rs[s]=rs[pre];sum[s]=sum[pre]+1;if(l<r){if(x<=mid) ls[s]=update(ls[pre],l,mid,x);else rs[s]=update(rs[pre],mid+1,r,x);}return s;}inline int sa(int u,int v,int l,int r,int k){if(l==r) return l;int x=sum[ls[v]]-sum[ls[u]];if(x>=k) return sa(ls[u],ls[v],l,mid,k);else return sa(rs[u],rs[v],mid+1,r,k-x);}int main(){scanf("%d%d",&n,&q);for(int i=1;i<=n;i++) scanf("%d",&a[i]),b[i]=a[i];sort(b+1,b+1+n);int m=unique(b+1,b+1+n)-b-1;rt[0]=build(1,m);for(int i=1;i<=n;i++){a[i]=lower_bound(b+1,b+1+m,a[i])-b;rt[i]=update(rt[i-1],1,m,a[i]);}for(int i=1;i<=q;i++){int x,y,z;scanf("%d%d%d",&x,&y,&z);printf("%d\n",b[sa(rt[x-1],rt[y],1,m,z)]);}}