uoj#207 共价大爷游长沙
题面:http://uoj.ac/problem/207
正解:$link-cut tree$
这题的正解比较玄学。。
我们可以对于每一条路径随机一个权值,两个端点分别异或这个权值。
于是判断一条边是否在所有路径上,只需判断其中一个点的子树异或和是不是等于所有路径的异或和就行了。这个正确率是很高的。。
然后就有一个问题了,如何用动态树来维护子树?
其实很简单。。我们可以另外维护一下一个结点所有虚儿子的子树异或和。这样,我们就可以维护这个点在$LCT$上整棵子树了。我们只需在$access$,$link$操作中加一些东西,就能维护虚子树了。
查询子树的时候,我们只需把当前点$access$,查询虚子树异或和,此时的虚子树一定是它在原树中的子树。一些细节详见代码注释。。
另外推荐一个讲得比较好的博客:http://blog.csdn.net/neither_nor/article/details/52979425
1 //It is made by wfj_2048~ 2 #include <algorithm> 3 #include <iostream> 4 #include <complex> 5 #include <cstring> 6 #include <cstdlib> 7 #include <cstdio> 8 #include <vector> 9 #include <cmath> 10 #include <queue> 11 #include <stack> 12 #include <map> 13 #include <set> 14 #define inf (1<<30) 15 #define N (300010) 16 #define il inline 17 #define RG register 18 #define ll long long 19 #define File(s) freopen(s".in","r",stdin),freopen(s".out","w",stdout) 20 21 using namespace std; 22 23 struct edge{ int x,y,z; }e[N]; 24 25 int ch[N][2],fa[N],sum[N],val[N],rev[N],st[N],n,m,S,tot; 26 //val为x和虚子树的异或和,sum为x在lct中子树异或和。 27 28 il int gi(){ 29 RG int x=0,q=1; RG char ch=getchar(); 30 while ((ch<'0' || ch>'9') && ch!='-') ch=getchar(); 31 if (ch=='-') q=-1,ch=getchar(); 32 while (ch>='0' && ch<='9') x=x*10+ch-48,ch=getchar(); 33 return q*x; 34 } 35 36 il int isroot(RG int x){ 37 return ch[fa[x]][0]!=x && ch[fa[x]][1]!=x; 38 } 39 40 il void pushup(RG int x){ 41 sum[x]=sum[ch[x][0]]^sum[ch[x][1]]^val[x]; return; 42 } 43 44 il void pushdown(RG int x){ 45 rev[x]=0,swap(ch[x][0],ch[x][1]); 46 rev[ch[x][0]]^=1,rev[ch[x][1]]^=1; return; 47 } 48 49 il void rotate(RG int x){ 50 RG int y=fa[x],z=fa[y],k=ch[y][0]==x; 51 if (!isroot(y)) ch[z][ch[z][1]==y]=x; 52 fa[x]=z,ch[y][k^1]=ch[x][k],fa[ch[x][k]]=y; 53 fa[y]=x,ch[x][k]=y,pushup(y),pushup(x); return; 54 } 55 56 il void splay(RG int x){ 57 RG int top=0; st[++top]=x; 58 for (RG int i=x;!isroot(i);i=fa[i]) st[++top]=fa[i]; 59 for (RG int i=top;i;--i) if (rev[st[i]]) pushdown(st[i]); 60 while (!isroot(x)){ 61 RG int y=fa[x],z=fa[y]; 62 if (!isroot(y)){ 63 if ((ch[z][0]==y)^(ch[y][0]==x)) rotate(x); 64 else rotate(y); 65 } 66 rotate(x); 67 } 68 return; 69 } 70 71 //access的时候,我们需要改变虚子树异或和。 72 il void access(RG int x){ 73 RG int t=0; 74 while (x){ 75 splay(x),val[x]^=sum[ch[x][1]]; 76 val[x]^=sum[ch[x][1]=t]; 77 pushup(x),t=x,x=fa[x]; 78 } 79 return; 80 } 81 82 il void makeroot(RG int x){ 83 access(x),splay(x),rev[x]^=1; return; 84 } 85 86 //link的时候,y也要makeroot,不然没法维护y的祖先的val和sum。 87 il void link(RG int x,RG int y){ 88 makeroot(x),makeroot(y),fa[x]=y; 89 val[y]^=sum[x],pushup(y); return; 90 } 91 92 il void cut(RG int x,RG int y){ 93 makeroot(x),access(y),splay(y); 94 ch[y][0]=fa[x]=0,pushup(y); return; 95 } 96 97 il void update(RG int x,RG int v){ 98 access(x),splay(x),val[x]^=v,sum[x]^=v; return; 99 } 100 101 //access以后,val维护的就是原子树异或和。 102 il int query(RG int x,RG int y){ 103 makeroot(x),access(y); 104 return val[y]==S ? 1 : 0; 105 } 106 107 il void work(){ 108 n=gi(),n=gi(),m=gi(); 109 for (RG int i=1,x,y;i<n;++i) 110 x=gi(),y=gi(),link(x,y); 111 for (RG int i=1,x,y,z,opt;i<=m;++i){ 112 opt=gi(); 113 if (opt==1){ 114 x=gi(),y=gi(),cut(x,y); 115 x=gi(),y=gi(),link(x,y); 116 } 117 if (opt==2){ 118 x=gi(),y=gi(); 119 e[++tot]=(edge){x,y,z=rand()}; 120 update(x,z),update(y,z),S^=z; 121 } 122 if (opt==3){ 123 x=gi(),S^=e[x].z; 124 update(e[x].x,e[x].z),update(e[x].y,e[x].z); 125 } 126 if (opt==4){ 127 x=gi(),y=gi(); 128 puts(query(x,y) ? "YES" : "NO"); 129 } 130 } 131 return; 132 } 133 134 int main(){ 135 File("changsha"); 136 srand(time(NULL)); 137 work(); 138 return 0; 139 }