顾名思义,主席树就是主席想出来的一种数据结构。
经典例题
主席树,大名“可持久化线段树”。线段树大家都知道,“可持久化”即为可以访问某一版本下的树。
很容易想到,可以开 棵线段树来实现可持久化线段树,可是这样的空间复杂度太高了,达到了 。
但是容易发现,每一次单点更新操作,最多只会有 个节点被更改。所以对于每次单点更新操作,可以只新建出 个节点,以节省空间。
例如这是一颗线段树:(第一个数代表版本号,第二、三个数代表区间)

假设我们需要修改位置 3
那么就需要新增一些节点:

假设我们又需要修改位置 5
那么又需要新增一些节点:

所以主席树这玩意根本不是树嘛……
怎么样,这种沿用旧节点的思想是不是很奇妙 awa。
可持久化线段树的难点主要在修改,查询基本和线段树是一样的,只不过多了版本号,需要记下每个版本对应的根节点。
给出模板题代码:
#include <iostream>
#include <cstdio>
#define S 1000005
using namespace std;
struct node
{
int l,r,num;
}tree[S*20];
int n,m,cnt,a[S],r[S];
void built(int &now,int l,int r)
{
if(l==r)
{
tree[++cnt].num=a[l];
now=cnt;
return;
}
int mid=l+r>>1;
now=++cnt;
built(tree[now].l,l,mid);
built(tree[now].r,mid+1,r);
}
void upd(int &now,int las,int l,int r,int wh,int val)
{
tree[++cnt]=tree[las];
now=cnt;
if(l==r)
{
tree[now].num=val;
return;
}
int mid=l+r>>1;
if(wh<=mid)
{
upd(tree[now].l,tree[las].l,l,mid,wh,val);
}
else
{
upd(tree[now].r,tree[las].r,mid+1,r,wh,val);
}
}
int que(int now,int l,int r,int wh)
{
if(l==r)
{
return tree[now].num;
}
int mid=l+r>>1;
if(wh<=mid)
{
return que(tree[now].l,l,mid,wh);
}
else
{
return que(tree[now].r,mid+1,r,wh);
}
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
}
built(r[0],1,n);
for(int i=1;i<=m;i++)
{
int v,type,pos;
scanf("%d%d%d",&v,&type,&pos);
if(type==1)
{
int val;
scanf("%d",&val);
upd(r[i],r[v],1,n,pos,val);
}
else
{
printf("%d\n",que(r[v],1,n,pos));
r[i]=r[v];
}
}
return 0;
}