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 }
原文地址:https://www.cnblogs.com/wfj2048/p/6920690.html