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");
}
}
}