[card title=“主席树” color=“info”]主席树,又叫可持久化权值线段树,也叫函数式线段树,是可持久化线段树的子集。在本文中,我们可以认为主席树等于可持久化线段树[/card]
可持久化线段树简介
基本结构、特点、作用在这篇文章中已经提到过:线段树扩展:权值线段树
总的来说就是每次修改或插入一个值,就新建一个根节点,并且向下递归去新建其他节点。
优点解释
每次插入操作最多创建的节点数都为
可持久化线段树基本操作
数据离散化
因为权值线段树的节点范围是一个值域,因此在值域非常大的时候需要进行离散化处理。
离散化过程很简单,先将a[]中的元素复制一份到lower_bound()
将原序列转换为去重后序列的下标即可。
建立
创建可持久化线段树的过程,相当于把
for(int i=1;i<=n;i++){ update(rt[i],rt[i-1],1,tot,lower_bound(b+1,b+tot+1,a[i])-b);}
这里的lower_bound(b+1,b+tot+1,a[i])-b
是将
因为这里我们用了前缀和的思想,所以
插入操作
简单叙述
插入元素时,只需要创建更新的节点,对无须更新的节点重用上一个版本(注意:不可对历史版本进行修改)。
例如,原序列
具体过程
-
插入元素
。复制上一版本 ,树根区间为 ,权值加 , ,这里 ,将其插入左子树中;复制上一个版本的节点 ,权值加 , ,这里 ,将其加入左子树中,复制上一个版本的节点 ,权值加 。此时已经加到了叶子节点,处理完毕。 -
插入元素
。复制上一版本 ,权值加 , ,这里 ,将其插入左子树;复制上一版本的节点 ,权值加 , , ,将其插入左子树;复制上一版本的节点 ,权值加 , , ,将其插入左子树;复制上次版本的节点 ,权值加 .此时已经加到了叶子节点,处理完毕。
void update(int &i,int j,int l,int r,int k){ i=++cnt; tree[i]=tree[j]; tree[i].num++; if(l==r) return; int mid=(l+r)>>1; if(k<=mid) update(tree[i].lc,tree[j].lc,l,mid,k); else update(tree[i].rc,tree[j].rc,mid+1,r,k); return;}
其中
可持久化线段树应用
求区间第 小的数
原理
在可持久化线段树中,有相同值域的节点有可减性。
- 以
为根的线段树,其权值表示序列 有有多少个数落入了 区间。 - 以
为根的线段树,其权值表示序列 有多少个数落入了 区间。
两棵线段树的值域划分是相同的,即两棵线段树中的节点是一一对应的。有相同值域的节点有可减性。
查询[i,j]区间第k小元素的时候,只需要将
步骤
当我要查询区间l==r
,则返回
int search(int i,int j,int l,int r,int k){ if(l==r) return l; int s=tree[tree[j].lc].num-tree[tree[i].lc].num; int mid=(l+r)>>1; if(k<=s) return search(tree[i].lc,tree[j].lc,l,mid,k); else return search(tree[i].rc,tree[j].rc,mid+1,r,k-s);}
分析
区间查询从根节点到叶子节点最多查询
而线段树套平衡树可以在
代码
来源:洛谷:P3834 【模板】可持久化线段树 2
#include<iostream>#include<cstdio>#include<algorithm>#include<cstring>#include<cmath>#include<vector>#include<queue>#include<map>#include<set>using namespace std;#define LL long long#define uLL unsigned long long#define reg register#define PI acos(-1.0)#define pb(x) push_back(x)#define mp(a,b) make_pair(a,b)#define fi first#define se second#define pr(x) cerr<<#x<<"="<<(x)<<endl#define pri(x,lo) {cerr<<#x<<"={";for (int ol=0;ol<=lo;ol++)cerr<<x[ol]<<",";cerr<<"}"<<endl;}#define inf 100000000#define N 1000#define M 10000001template<class T>inline void read(T &x){ x=0;register char c=getchar();register bool f=0; while(!isdigit(c))f^=c=='-',c=getchar(); while(isdigit(c))x=(x<<3)+(x<<1)+(c^48),c=getchar(); if(f)x=-x;}template<class T>inline void print(T x){ if(x<0)putchar('-'),x=-x; if(x>9)print(x/10); putchar('0'+x%10);}struct Node{ int lc,rc,num;}tree[M];int a[M],b[M],rt[M],n,m,cnt;void update(int &i,int j,int l,int r,int k){ i=++cnt; tree[i]=tree[j]; tree[i].num++; if(l==r) return; int mid=(l+r)>>1; if(k<=mid) update(tree[i].lc,tree[j].lc,l,mid,k); else update(tree[i].rc,tree[j].rc,mid+1,r,k); return;}int search(int i,int j,int l,int r,int k){ if(l==r) return l; int s=tree[tree[j].lc].num-tree[tree[i].lc].num; int mid=(l+r)>>1; if(k<=s) return search(tree[i].lc,tree[j].lc,l,mid,k); else return search(tree[i].rc,tree[j].rc,mid+1,r,k-s);}int main(){ read(n),read(m); for(int i=1;i<=n;i++) { read(a[i]); b[i]=a[i]; } sort(b+1,b+1+n); int tot=unique(b+1,b+1+n)-b-1; for(int i=1;i<=n;i++) { update(rt[i],rt[i-1],1,tot,lower_bound(b+1,b+tot+1,a[i])-b); } for(int i=0;i<=n;i++) { pr(rt[i]); } for(int i=1;i<=m;i++) { int l,r,k; read(l),read(r),read(k); int ans=search(rt[l-1],rt[r],1,tot,k); print(b[ans]); putchar('\n'); } return 0;}