题意:动态树上支持加入路径、删除路径、询问一条边是否被当时存在的所有路径经过?
标程:
1 #include<bits/stdc++.h> 2 #define P pair<int,int> 3 using namespace std; 4 int read() 5 { 6 int x=0;char ch=getchar(); 7 while (ch<'0'||ch>'9') ch=getchar(); 8 while ('0'<=ch&&ch<='9') x=(x<<1)+(x<<3)+ch-'0',ch=getchar(); 9 return x; 10 } 11 const int N=300005; 12 P e[N]; 13 int son[N][2],fa[N],top,q[N],rev[N],sum[N],lsum[N],val[N],n,Q,x,y,op,w[N],u,v,cnt,s; 14 bool is_rt(int x){return son[fa[x]][0]!=x&&son[fa[x]][1]!=x;} 15 void up(int x) 16 { 17 sum[x]=val[x]^lsum[x]; 18 sum[x]^=sum[son[x][0]]^sum[son[x][1]]; 19 } 20 void down(int x) 21 { 22 if (rev[x]) 23 { 24 rev[son[x][0]]^=1;rev[son[x][1]]^=1;rev[x]^=1; 25 swap(son[x][0],son[x][1]); 26 } 27 } 28 void rot(int x) 29 { 30 int y=fa[x],z=fa[y],l=(son[y][1]==x),r=l^1; 31 if (!is_rt(y)) son[z][son[z][1]==y]=x; 32 fa[x]=z;fa[y]=x;fa[son[x][r]]=y; 33 son[y][l]=son[x][r];son[x][r]=y; 34 up(y);up(x); 35 } 36 void spl(int x) 37 { 38 q[top=1]=x; 39 for (int i=x;!is_rt(i);i=fa[i]) q[++top]=fa[i]; 40 while (top) down(q[top--]); 41 for (int y;!is_rt(x);rot(x))//注意这里的y要在内部定义,否则会更改掉外面的y 42 { 43 if (!is_rt(y=fa[x])) 44 rot(((son[y][0]==x)^(son[fa[y]][0]==y))?x:y); 45 } 46 } 47 void accs(int x) { 48 for (int t=0;x;t=x,x=fa[x]) 49 { 50 spl(x); 51 lsum[x]^=sum[son[x][1]];//原来的重儿子变成轻儿子,加入 52 lsum[x]^=sum[son[x][1]=t];//去掉原来的轻儿子 53 up(x); 54 } 55 } 56 void make_rt(int x) {accs(x);spl(x);rev[x]^=1;} 57 void cut(int x,int y){make_rt(x);accs(y);spl(y);fa[x]=son[y][0]=0;up(y);} 58 void link(int x,int y){make_rt(x);fa[x]=y;make_rt(y);lsum[y]^=sum[x];up(y);} 59 void mdi(int x,int v) {make_rt(x);val[x]^=v;up(x);} 60 int main() 61 { 62 int id=read();srand(time(NULL)); 63 n=read();Q=read(); 64 for (int i=1;i<n;i++) u=read(),v=read(),link(u,v); 65 while (Q--) 66 { 67 op=read(); 68 if (op==1) 69 { 70 x=read();y=read();cut(x,y); 71 x=read();y=read();link(x,y); 72 }else if (op==2){ 73 x=read();y=read();w[++cnt]=rand()*131; 74 mdi(x,w[cnt]);mdi(y,w[cnt]); 75 s^=w[cnt];e[cnt]=P(x,y); 76 }else if (op==3) { 77 x=read();s^=w[x]; 78 mdi(e[x].first,w[x]);mdi(e[x].second,w[x]); 79 } 80 else { 81 x=read();y=read(); 82 make_rt(x);accs(y);spl(y); 83 puts(sum[x]==s?"YES":"NO"); 84 } 85 } 86 return 0; 87 }
题解:lct+随机权值
对每条路径随机一个权值,并在两端点处赋值。
询问时如果子树中的权值异或和=所有路径的异或和,那么大概率(x,y)这条边被所有路径经过。
sum表示整个子树的异或和,lsum表示轻儿子子树的异或和。由于重儿子在lct上会不断改变,而lsum只有在accs重连和link时会被改变。
link时修改lsum[y],需要make_rt(y),不然需要改变y到根的链上所有点的sum。