题解 P5055 【【模板】可持久化文艺平衡树】
zhongyuwei · · 题解
我用了一种类似于支持区间修改的可持久化线段树的方法,也就是标记永久化。看到大家的做法都是在下放标记的时候把节点复制一遍,觉得这样很不爽啊……我想,能不能干脆把标记永久化,节约一些空间和时间呢?
实现的关键在于merge和split这两个函数。
split
假设我们现在要把以
我们记
那么,假如
左儿子的大小小于
注意,如果从根节点到某个点
int tx,ta,tb;
void split(int x,int k,int &a,int &b)
{
if(!x){a=b=0; return;} tx^=rev[x];
if(sz[ch[x][tx]]>=k)
{
rev[b=cpy(x)]=tb^tx,tb=tx;
split(ch[x][tx],k,a,ch[b][tb]),push_up(b);
}
else
{
rev[a=cpy(x)]=ta^tx,ta=tx;
split(ch[x][tx^1],k-sz[ch[x][tx]]-1,ch[a][ta^1],b),push_up(a);
}
}
merge
我们假设现在要把第一棵
我们仍然记
假如现在
对于
注意我们
int tx,ta,tb;
void merge(int &r,int a,int b)
{
if(!a||!b)
{
if(a) ta^=rev[a],rev[r=a]=tx^ta;
else if(b) tb^=rev[b],rev[r=b]=tx^tb;
else r=0;
return;
}
if(key[a]<=key[b])
{
ta^=rev[a],rev[r=a]=tx^ta,tx=ta;
merge(ch[r][tx^1],ch[a][ta^1],b);
}
else
{
tb^=rev[b],rev[r=b]=tx^tb,tx=tb;
merge(ch[r][tx],a,ch[b][tb]);
}
push_up(r);
}
下面是我巨丑无比的完整代码:
#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#define ll long long
#define uint unsigned int
#define done tx=ta=tb=0
using namespace std;
template <class T>
inline void read(T &x)
{
x=0; char c=getchar(); int f=1;
while(!isdigit(c)){if(c=='-')f=-1; c=getchar();}
while(isdigit(c)) x=x*10-'0'+c,c=getchar(); x*=f;
}
uint seed=19260817;
uint Rand()
{
seed^=seed<<13,seed^=seed>>17,seed^=seed<<5;
return seed;
}
const int N=2e5+10,M=N*66;
int rt[N],ch[M][2],val[M],rev[M],sz[M],ncnt;
ll sum[M];
uint key[M];
void push_up(int x){sz[x]=sz[ch[x][0]]+sz[ch[x][1]]+1,sum[x]=sum[ch[x][0]]+sum[ch[x][1]]+val[x];}
int stk[M],top;
int make(int v)
{
int u=top?stk[top--]:++ncnt;
ch[u][0]=ch[u][1]=rev[u]=0;
val[u]=sum[u]=v,key[u]=Rand(),sz[u]=1;
return u;
}
int cpy(int c)
{
int u=top?stk[top--]:++ncnt;
ch[u][0]=ch[c][0],ch[u][1]=ch[c][1],rev[u]=rev[c];
val[u]=val[c],sum[u]=sum[c],key[u]=key[c],sz[u]=sz[c];
return u;
}
int tx,ta,tb;
void merge(int &r,int a,int b)
{
if(!a||!b)
{
if(a) ta^=rev[a],rev[r=a]=tx^ta;
else if(b) tb^=rev[b],rev[r=b]=tx^tb;
else r=0;
return;
}
if(key[a]<=key[b])
{
ta^=rev[a],rev[r=a]=tx^ta,tx=ta;
merge(ch[r][tx^1],ch[a][ta^1],b);
}
else
{
tb^=rev[b],rev[r=b]=tx^tb,tx=tb;
merge(ch[r][tx],a,ch[b][tb]);
}
push_up(r);
}
void split(int x,int k,int &a,int &b)
{
if(!x){a=b=0; return;} tx^=rev[x];
if(sz[ch[x][tx]]>=k)
{
rev[b=cpy(x)]=tb^tx,tb=tx;
split(ch[x][tx],k,a,ch[b][tb]),push_up(b);
}
else
{
rev[a=cpy(x)]=ta^tx,ta=tx;
split(ch[x][tx^1],k-sz[ch[x][tx]]-1,ch[a][ta^1],b),push_up(a);
}
}
void insert(int r1,int &r2,int p,int v)
{
int x,y; done; split(r1,p,x,y);
done; merge(r2,x,make(v)); done; merge(r2,r2,y);
}
void del(int r1,int &r2,int p)
{
int x,y,z; done; split(r1,p,x,y); done; split(x,p-1,x,z);
stk[++top]=z; done; merge(r2,x,y);
}
void Reverse(int r1,int &r2,int l,int r)
{
int x,y,z; done; split(r1,l-1,x,y); done; split(y,r-l+1,y,z);
rev[y]^=1; done; merge(r2,x,y); done; merge(r2,r2,z);
}
ll query(int r1,int l,int r)
{
int x,y,z; done; split(r1,l-1,x,y); done; split(y,r-l+1,y,z);
return sum[y];
}
int main()
{
int q,v,op,x,l,r; ll lst=0; read(q);
for(int i=1;i<=q;++i)
{
read(v),read(op);
if(op==1)
{
read(l),read(x); l^=lst,x^=lst;
insert(rt[v],rt[i],l,x);
}
else if(op==2)
{
read(l); l^=lst;
del(rt[v],rt[i],l);
}
else if(op==3)
{
read(l),read(r); l^=lst,r^=lst;
Reverse(rt[v],rt[i],l,r);
}
else
{
read(l),read(r); l^=lst,r^=lst;
printf("%lld\n",lst=query(rt[v],l,r)),rt[i]=rt[v];
}
}
return 0;
}