您的位置:首页 > 其它

uoj#207 共价大爷游长沙

2017-05-30 16:29 302 查看
题面:http://uoj.ac/problem/207

正解:$link-cut tree$

这题的正解比较玄学。。

我们可以对于每一条路径随机一个权值,两个端点分别异或这个权值。

于是判断一条边是否在所有路径上,只需判断其中一个点的子树异或和是不是等于所有路径的异或和就行了。这个正确率是很高的。。

然后就有一个问题了,如何用动态树来维护子树?

其实很简单。。我们可以另外维护一下一个结点所有虚儿子的子树异或和。这样,我们就可以维护这个点在$LCT$上整棵子树了。我们只需在$access$,$link$操作中加一些东西,就能维护虚子树了。

查询子树的时候,我们只需把当前点$access$,查询虚子树异或和,此时的虚子树一定是它在原树中的子树。一些细节详见代码注释。。

另外推荐一个讲得比较好的博客:http://blog.csdn.net/neither_nor/article/details/52979425

//It is made by wfj_2048~
#include <algorithm>
#include <iostream>
#include <complex>
#include <cstring>
#include <cstdlib>
#include <cstdio>
#include <vector>
#include <cmath>
#include <queue>
#include <stack>
#include <map>
#include <set>
#define inf (1<<30)
#define N (300010)
#define il inline
#define RG register
#define ll long long
#define File(s) freopen(s".in","r",stdin),freopen(s".out","w",stdout)

using namespace std;

struct edge{ int x,y,z; }e
;

int ch
[2],fa
,sum
,val
,rev
,st
,n,m,S,tot;
//val为x和虚子树的异或和,sum为x在lct中子树异或和。

il int gi(){
RG int x=0,q=1; RG char ch=getchar();
while ((ch<'0' || ch>'9') && ch!='-') ch=getchar();
if (ch=='-') q=-1,ch=getchar();
while (ch>='0' && ch<='9') x=x*10+ch-48,ch=getchar();
return q*x;
}

il int isroot(RG int x){
return ch[fa[x]][0]!=x && ch[fa[x]][1]!=x;
}

il void pushup(RG int x){
sum[x]=sum[ch[x][0]]^sum[ch[x][1]]^val[x]; return;
}

il void pushdown(RG int x){
rev[x]=0,swap(ch[x][0],ch[x][1]);
rev[ch[x][0]]^=1,rev[ch[x][1]]^=1; return;
}

il void rotate(RG int x){
RG int y=fa[x],z=fa[y],k=ch[y][0]==x;
if (!isroot(y)) ch[z][ch[z][1]==y]=x;
fa[x]=z,ch[y][k^1]=ch[x][k],fa[ch[x][k]]=y;
fa[y]=x,ch[x][k]=y,pushup(y),pushup(x); return;
}

il void splay(RG int x){
RG int top=0; st[++top]=x;
for (RG int i=x;!isroot(i);i=fa[i]) st[++top]=fa[i];
for (RG int i=top;i;--i) if (rev[st[i]]) pushdown(st[i]);
while (!isroot(x)){
RG int y=fa[x],z=fa[y];
if (!isroot(y)){
if ((ch[z][0]==y)^(ch[y][0]==x)) rotate(x);
else rotate(y);
}
rotate(x);
}
return;
}

//access的时候,我们需要改变虚子树异或和。
il void access(RG int x){
RG int t=0;
while (x){
splay(x),val[x]^=sum[ch[x][1]];
val[x]^=sum[ch[x][1]=t];
pushup(x),t=x,x=fa[x];
}
return;
}

il void makeroot(RG int x){
access(x),splay(x),rev[x]^=1; return;
}

//link的时候,y也要makeroot,不然没法维护y的祖先的val和sum。
il void link(RG int x,RG int y){
makeroot(x),makeroot(y),fa[x]=y;
val[y]^=sum[x],pushup(y); return;
}

il void cut(RG int x,RG int y){
makeroot(x),access(y),splay(y);
ch[y][0]=fa[x]=0,pushup(y); return;
}

il void update(RG int x,RG int v){
access(x),splay(x),val[x]^=v,sum[x]^=v; return;
}

//access以后,val维护的就是原子树异或和。
il int query(RG int x,RG int y){
makeroot(x),access(y);
return val[y]==S ? 1 : 0;
}

il void work(){
n=gi(),n=gi(),m=gi();
for (RG int i=1,x,y;i<n;++i)
x=gi(),y=gi(),link(x,y);
for (RG int i=1,x,y,z,opt;i<=m;++i){
opt=gi();
if (opt==1){
x=gi(),y=gi(),cut(x,y);
x=gi(),y=gi(),link(x,y);
}
if (opt==2){
x=gi(),y=gi();
e[++tot]=(edge){x,y,z=rand()};
update(x,z),update(y,z),S^=z;
}
if (opt==3){
x=gi(),S^=e[x].z;
update(e[x].x,e[x].z),update(e[x].y,e[x].z);
}
if (opt==4){
x=gi(),y=gi();
puts(query(x,y) ? "YES" : "NO");
}
}
return;
}

int main(){
File("changsha");
srand(time(NULL));
work();
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: