如果每次加入点对$(x,y)$,就给它一个随机的权值$v$,把两个点的点权都异或$v$,查询$(x,y)$的时候,只要把$x$硬点为根,以$y$为根的子树的异或和等于当前所有的异或和,那么很大概率就是正确的(每对点刚好有一个在$y$的子树中)
所以直接用lct维护即可,因为维护了虚边信息,所以link时两边都要makeroot(不然没法更新splay里的祖先的信息),cut时不改虚边信息
splay之前一定要记得一路pushdown啊啊啊啊啊啊!
#include<stdio.h> #include<stdlib.h> int ch[100010][2],fa[100010],r[100010],v[100010],s[100010],vs[100010],q1[300010],q2[300010],d[300010]; #define ls ch[x][0] #define rs ch[x][1] void swap(int&a,int&b){a^=b^=a^=b;} void rev(int x){ swap(ls,rs); r[x]^=1; } bool isrt(int x){return ch[fa[x]][0]!=x&&ch[fa[x]][1]!=x;} void pushdown(int x){ if(r[x]){ if(ls)rev(ls); if(rs)rev(rs); r[x]=0; } } void pushup(int x){s[x]=v[x]^s[ls]^s[rs]^vs[x];} void rot(int x){ int y,z,f,B; y=fa[x]; z=fa[y]; f=(ch[y][0]==x); B=ch[x][f]; fa[x]=z; fa[y]=x; if(B)fa[B]=y; ch[x][f]=y; ch[y][f^1]=B; if(ch[z][0]==y)ch[z][0]=x; if(ch[z][1]==y)ch[z][1]=x; pushup(y); pushup(x); } void gao(int x){ if(!isrt(x))gao(fa[x]); pushdown(x); } void splay(int x){ int y,z; gao(x); while(!isrt(x)){ y=fa[x]; z=fa[y]; if(!isrt(y))rot((ch[z][0]==y&&ch[y][0]==x)||(ch[z][1]==y&&ch[y][1]==x)?y:x); rot(x); } } void access(int x){ int y=0; while(x){ splay(x); vs[x]^=s[rs]; rs=y; vs[x]^=s[y]; pushup(x); y=x; x=fa[x]; } } void makert(int x){ access(x); splay(x); rev(x); } void link(int x,int y){ makert(x); makert(y); fa[x]=y; vs[y]^=s[x]; pushup(y); } void cut(int x,int y){ makert(y); access(x); splay(x); fa[y]=ls=0; pushup(x); } void modify(int x,int d){ access(x); splay(x); v[x]^=d; pushup(x); } int query(int x,int y){ makert(x); access(y); return vs[y]^v[y]; } int main(){ srand(19260817); int n,m,i,x,y,sum; scanf("%d%d%d",&i,&n,&m); for(i=1;i<n;i++){ scanf("%d%d",&x,&y); link(x,y); } n=sum=0; while(m--){ scanf("%d",&i); if(i==1){ scanf("%d%d",&x,&y); cut(x,y); scanf("%d%d",&x,&y); link(x,y); } if(i==2){ n++; scanf("%d%d",q1+n,q2+n); d[n]=x=rand()*rand(); sum^=x; modify(q1[n],x); modify(q2[n],x); } if(i==3){ scanf("%d",&x); modify(q1[x],d[x]); modify(q2[x],d[x]); sum^=d[x]; } if(i==4){ scanf("%d%d",&x,&y); puts(query(x,y)==sum?"YES":"NO"); } } }