主席树又名可持久化线段树。
我们先撇开可持久化看看主席树是怎么实现的。维护一个长度为n的序列,我们可以建一颗线段树来维护,最上的根节点代表的区间是[1,n]。
我们知道线段树的形态由你给的[l,r],既维护的区间长度决定,现在我要为这个序列的每一个前缀建一棵线段树,每一棵线段树最上的根节点代表的区间依然是[1,n],所以这n棵线段树的结构是一模一样的。
这里的线段树不是普通的线段树而是权值线段树,既它维护的不是序列里的每个ai的值而是整个序列的第几大。对每个ai,插入树中不是ai值而是它是第几大v,接下来从根节点开始v与对应区间的mid比较,小于等于mid就划到左子树,否则划到右子树,直到叶子节点。整个过程像极了二叉搜索树,可以类比理解。
说完建树我们来看看如何实现可持久化。根据clj的论文,可持久化数据结构(Persistent data structure)就是利用函数式编程的思想使其支持询问历史版本、同时充分利用它们之间的共同数据来减少时间和空间消耗。那么哪些地方可以“充分利用它们之间的共同数据”,第i棵线段树建树过程中一个v如果插到左子树,那么当前结点的右子树和第i-1棵线段树对应节点是一模一样的,所以我们只需要把第i棵线段树当前结点的右子树指向第i-1棵线段树对应节点就行了。如果插入右边,同理。这样就实现了可持久化。
下面是几个重要结论:
1、建第i棵线段树,只建了logn个节点,因为每次插入操作只产生一个节点另一个节点是复用的。
2、建树过程中第i棵线段树只与第i-1棵线段树有关。
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <cmath>
#include <cstring>
using namespace std;
const int maxn=1e5+7;
struct node{
int ls,rs,sum;
}data[maxn*20];
int cnt;
int root[maxn];
int a[maxn],b[maxn],mp[maxn];
int build(int l,int r){
int t=cnt++;
data[t].sum=0;
if(l==r)return t;
int mid=l+r>>1;
data[t].ls=build(l,mid);
data[t].rs=build(mid+1,r);
return t;
}
int update(int k,int l,int r,int v){
int t=cnt++;
data[t].sum=data[k].sum+1;
if(l==r)return t;
int mid=l+r>>1;
if(v<=mid){
data[t].rs=data[k].rs;
data[t].ls=update(data[k].ls,l,mid,v);
}
else {
data[t].ls=data[k].ls;
data[t].rs=update(data[k].rs,mid+1,r,v);
}
return t;
}
int query(int t1,int t2,int l,int r,int v){
if(l==r)return l;
int mid=l+r>>1;
int w=data[data[t2].ls].sum-data[data[t1].ls].sum;
if(v<=w) return query(data[t1].ls,data[t2].ls,l,mid,v);
else return query(data[t1].rs,data[t2].rs,mid+1,r,v-w);
}
void debug(int t,int l,int r){
printf("l==%d r==%d sum==%d\n",l,r,data[t].sum);
if(l==r)return ;
int mid=l+r>>1;
debug(data[t].ls,l,mid);
debug(data[t].rs,mid+1,r);
}
int main()
{
int n,m;
while(~scanf("%d%d",&n,&m)){
cnt=1;
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
b[i]=a[i];
}
sort(b+1,b+n+1);
int len=unique(b+1,b+1+n)-(b+1);
for(int i=1;i<=n;i++){
int t=lower_bound(b+1,b+1+len,a[i])-(b+1)+1;
mp[t]=a[i];a[i]=t;
}
root[0]=build(1,len);
for(int i=1;i<=n;i++){
root[i]=update(root[i-1],1,len,a[i]);
}
/*for(int i=0;i<=n;i++){
printf("----i==%d------\n",i);
debug(root[i],1,len);
}*/
for(int i=0;i<m;i++){
int l,r,v;
scanf("%d%d%d",&l,&r,&v);if(l>r)swap(l,r);
printf("%d\n",mp[query(root[l-1],root[r],1,len,v)]);
}
}
return 0;
}