主席树

主席树主要处理那些动态修改同时需要查询之前状态信息的题。
这种查询之前状态的操作就是我们常说的需要可持久化的数据结构维护信息的查询。
所以有一堆可持久化的数据结构,这里就只说可持久化线段树(主席树)。
主席树是在线段树的基础上,每一次更改操作时,对于更改的部分(整条链)直接暴力开一些新的节点。
这样对于每一次更改建立一个时间戳,就可以以每一次更改增加log n的抽象可承受空间复杂度,解决查询问题。
这时单次查询仍旧是log级别,只是空间大得离谱。
具体的细节就看代码吧,好像没有什么特别高深的东西(当然可能也是现在我理解的浅显)。
其实这个模板不用主席树更优,但先这样吧

#include<bits/stdc++.h>
typedef long long ll;
typedef unsigned long long ull;
#define qr qr()
#define ps push_back
#define pa pair<int,int>
#define ve vector
#define fi first
#define se second
using namespace std;
//N直接3e7就够,这里因为洛谷空间限制1GB随便开空间太爽了,所以放了个1e8.
const int N=1e8+200,MN=1e6+200;
int n,m,tot,num[MN],root[MN];
inline ll qr{
	ll x=0;char ch=getchar();bool f=0;
	while(ch>57||ch<48)f=(ch=='-')?1:0,ch=getchar();
	while(ch>=48&&ch<=57)x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
	return f?-x:x;
}
struct tr{
	int l,r,val,ls,rs;
}t[N];
inline int creat(int node){
	++tot;
	t[tot]=t[node];
	return tot;
}
void build(int &rt,int l,int r){
	rt=++tot;
	t[rt].l=l;
	t[rt].r=r;
	if(l==r)return (void)(t[rt].val=num[l]);
	int md=l+r>>1;
	build(t[rt].ls,l,md);
	build(t[rt].rs,md+1,r);
}
int update(int rt,int pos,int val){
	rt=creat(rt);
	if(t[rt].l==t[rt].r)t[rt].val=val;
	else{
		if(pos<=t[t[rt].ls].r)t[rt].ls=update(t[rt].ls,pos,val);
		else t[rt].rs=update(t[rt].rs,pos,val);
	}return rt;
}
int ask(int rt,int pos){
	if(t[rt].l==t[rt].r)return t[rt].val;
	else{
		if(pos<=t[t[rt].ls].r)return ask(t[rt].ls,pos);
		else return ask(t[rt].rs,pos);
	}
}
void init(){
	n=qr;m=qr;
	for(int i=1;i<=n;++i)num[i]=qr;
	build(root[0],1,n);
	int bl,pos,val,op;
	for(int i=1;i<=m;++i){
		bl=qr,op=qr,pos=qr;
		if(op&1){
			val=qr;
			root[i]=update(root[bl],pos,val);
		}else{
			cout<<ask(root[bl],pos)<<'\n';
			root[i]=root[bl];
		}
	}
}
int main(){
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);




	freopen("in.in","r",stdin);
	freopen("out.out","w",stdout);




	init();
	return 0;
}

另有这个模板,是在主席树上作按秩合并并查集。
注意按秩合并时要用dep合并,不可用size去合并,不然常数会爆炸(因为每一次更改一定比dep的多一个log)。
当然这个模板仍然是离线的非主席树做法更优...

#include<bits/stdc++.h>
typedef long long ll;
typedef unsigned long long ull;
#define qr qr()
#define ps push_back
#define pa pair<int,int>
#define ve vector
#define fi first
#define se second
#define lc t[rt].ls
#define rc t[rt].rs
using namespace std;
const int N=3e5+200;
int n,m,root[N],cnt;
inline ll qr{
	ll x=0;char ch=getchar();
	while(ch>57||ch<48)ch=getchar();
	while(ch>=48&&ch<=57)x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
	return x;
}
struct tr{
	int l,r,fa,ls,rs,dep;
}t[N*25];
inline int build(int l,int r){
	int rt=++cnt;
	t[rt].l=l;
	t[rt].r=r;
	if(l==r){
		t[rt].fa=l;
		return rt;
	}int md=l+r>>1;
	lc=build(l,md);
	rc=build(md+1,r);
	return rt;
}
inline int ask(int rt,int pos){
	if(t[rt].l==t[rt].r)return rt;
	if(t[lc].r>=pos)return ask(lc,pos);
	else return ask(rc,pos);
}
inline int find(int rt,int x){
	int wh=ask(rt,x);
	if(t[wh].fa==x)return wh;
	else return find(rt,t[wh].fa);
}
inline int creat(int rt){
	int now=++cnt;
	t[now]=t[rt];
	return now;
}
inline int change(int rt,int pos,int y){
	int now=creat(rt);
	if(t[rt].l==t[rt].r){
		t[now].fa=y;
		return now;
	}
	if(t[lc].r>=pos)t[now].ls=change(lc,pos,y);
	else t[now].rs=change(rc,pos,y);
	return now;
}
inline int add(int rt,int x){
	int now=creat(rt);
	if(t[rt].l==t[rt].r){
		++t[now].dep;
		return now;
	}
	if(t[lc].r>=x)t[now].ls=add(lc,x);
	else t[now].rs=add(rc,x);
	return now;
}
inline int merge(int rt,int x,int y){
	int a=find(rt,x),b=find(rt,y);
	if(a==b)return rt;
	if(t[a].dep<t[b].dep)swap(a,b);
	int newrt=change(rt,t[b].fa,t[a].fa);
	return(t[a].dep==t[b].dep)?add(newrt,t[a].fa):newrt;
}
void init(){
	n=qr;m=qr;
	root[0]=build(1,n);
	for(int i=1,op,x,y,k,nrt=1;i<=m;++i){
		op=qr;
		if(op&1){
			x=qr,y=qr;
			if(op==1)
				root[i]=nrt=merge(nrt,x,y);
			else if(op==3)
				root[i]=nrt,
				cout<<(t[find(nrt,x)].fa==t[find(nrt,y)].fa)<<'\n';
		}else k=qr,root[i]=nrt=root[k];
	}
}
int main(){
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);




	freopen("in.in","r",stdin);
	freopen("out.out","w",stdout);




	init();
	return 0;
}
posted @ 2024-08-09 18:52  SLS-wwppcc  阅读(20)  评论(0)    收藏  举报