浅谈主席树
距离CSP2019只有不到二十天了……就简单说说,然后贴个代码吧
可持久化线段树,又名主席树(因为提出这个数据结构的人叫hjt)
可持久化,即为可以追溯某个历史版本
你需要维护这样的一个长度为 N 的数组,支持如下几种操作
-
在某个历史版本上修改某一个位置上的值
-
访问某个历史版本上的某一位置的值
此外,每进行一次操作(对于操作2,即为生成一个完全一样的版本,不作任何改动),就会生成一个新的版本。版本编号即为当前操作的编号(从1开始编号,版本0表示初始状态数组)
详见代码和注释
#include<iostream> #include<cstdio> #include<cstdlib> #include<cstring> #include<algorithm> #include<cmath> #define mid (l+r>>1) #define maxn 1000005 using namespace std; inline int read() { int f=1,x=0; char ch=getchar(); while(!isdigit(ch)) {if(ch=='-') f=-1; ch=getchar();} while(isdigit(ch)) {x=x*10+ch-'0'; ch=getchar();} return x*f; } int n,m,cnt; int a[maxn],t[maxn];//t:每颗线段树的编号 int lc[maxn<<5],rc[maxn<<5],tre[maxn<<5];//lc:左儿子 rc:右儿子 tre:结点权值(需要注意的是,主席树的左右儿子不能直接用乘2和cheng2加1求出,必须存储)(注意2:主席树一般开20倍空间以上) int build(int pre,int l,int r)//建树 { int rt=++cnt; if(l==r) { tre[rt]=a[l]; return rt; } lc[rt]=build(lc[rt],l,mid); rc[rt]=build(rc[rt],mid+1,r); return rt; } int update(int pre,int l,int r,int p,int x)//单点修改 { int rt=++cnt; lc[rt]=lc[pre],rc[rt]=rc[pre],tre[rt]=tre[pre];//当前版本继承历史版本的信息 if(l==r) { tre[rt]=x; return rt; } if(p<=mid) lc[rt]=update(lc[rt],l,mid,p,x);//创建新的版本 else rc[rt]=update(rc[rt],mid+1,r,p,x); return rt; } int query(int pre,int l,int r,int p)//单点查询 { if(l==r) { return tre[pre]; } if(p<=mid) return query(lc[pre],l,mid,p); else return query(rc[pre],mid+1,r,p); } int main() { int i; int k,x,y,z; n=read(); m=read(); for(i=1;i<=n;i++) a[i]=read(); t[0]=build(0,1,n);//建立最开始的版本 for(i=1;i<=m;i++) { x=read(); k=read(); if(k==1) { y=read(); z=read(); t[i]=update(t[x],1,n,y,z); } if(k==2) { y=read(); printf("%d\n",query(t[x],1,n,y)); t[i]=t[x];//询问也要新建状态(详见题目) } } return 0; }
CSP2019 RP++!