miaom又来做LCT了!//lych:无敌
由于不是很懂LCT子树信息维护的那套理论,想了好久(搞得树剖的子树维护就会了一样!)。
首先是一个很巧妙的转化——将边在链上转化为这条边能将链的顶点分开。给每组顶赋一个随机权,每次可以把边断掉,查询每个联通块内权值异或和是否等于所有顶点权值异或和,就是子树异或和。
然后发现动态树上子树不是dfs序连续那么简单,他应该是(当前点及沿偏爱边往下走得到的点)以及他们的虚边的子树。然后维护一些值(splay 区间虚儿子&自己和
单点&虚儿子和)。
LCT的代码来自@lbn187板子的默写,如有错误不负责。。
#include<iostream>
#include<cstdio>
#include<cstdlib>
#define N 300005
using namespace std;
int id,n,m,x,y,p,qx[N],qy[N],now,val[N],ly;
int c[N][2],fa[N],v[N],s[N],rev[N];
//子树权值异或和
//splay 区间虚儿子&自己和 s
//单点&虚儿子和 v
//单点修改
bool RT(int x){return c[fa[x]][0]!=x&&c[fa[x]][1]!=x;}
void rever(int x){rev[x]^=1;swap(c[x][0],c[x][1]);}
void up(int x)
{
s[x]=s[c[x][0]]^s[c[x][1]]^v[x];
}
void dn(int x)
{
if (rev[x])
rev[x]=0,rever(c[x][0]),rever(c[x][1]);
}
void pd(int x){if (!RT(x)) pd(fa[x]);dn(x);}
void rot(int x)
{
int y=fa[x],z=fa[y],l=c[y][1]==x,r=l^1;
if (!RT(y)) c[z][c[z][1]==y]=x;
fa[x]=z;fa[y]=x;fa[c[x][r]]=y;
c[y][l]=c[x][r];c[x][r]=y;up(y);up(x);
}
void splay(int x,int y=0)
{
for (pd(x);!RT(x);rot(x))
if (!RT(y=fa[x]))
rot(c[y][0]==x^c[fa[y]][0]==y?x:y);
}
void acc(int x)
{
for (int y=0;x;y=x,x=fa[x])
splay(x),v[x]^=s[c[x][1]]^s[y],c[x][1]=y,up(x);
}
void MRt(int x){acc(x);splay(x);rever(x);}
void link(int x,int y){MRt(x);MRt(y);v[y]^=s[x];s[y]^=s[x];fa[x]=y;}
void cut(int x,int y){MRt(x);acc(y);splay(y);c[y][0]=fa[x]=0;up(y);}
void mdy(int x,int y){MRt(x);v[x]^=y;s[x]^=y;}
int qry(int x,int y){MRt(x);acc(y);return v[y]+s[c[y][1]];}
int main()
{
int id;
scanf("%d",&id);
scanf("%d%d",&n,&m);
for (int i=2;i<=n;i++)
scanf("%d%d",&x,&y),link(x,y);
for (int i=1;i<=m;i++)
{
scanf("%d",&p);
if (p==1)
{
scanf("%d%d",&x,&y);
cut(x,y);
scanf("%d%d",&x,&y);
link(x,y);
}
else if (p==2)
{
scanf("%d%d",&x,&y);
val[++ly]=(rand()<<16)^rand();
qx[ly]=x;qy[ly]=y;
now^=val[ly];
mdy(x,val[ly]);
mdy(y,val[ly]);
}
else if (p==3)
{
scanf("%d",&x);
now^=val[x];
mdy(qx[x],val[x]);
mdy(qy[x],val[x]);
}
else if (p==4)
{
scanf("%d%d",&x,&y);
puts(qry(x,y)==now?"YES":"NO");
}
}
}