【UOJ#207】共价大爷游长沙
题目描述
火车司机出秦川,跳蚤国王下江南,共价大爷游长沙。每个周末,勤劳的共价大爷都会开车游历长沙市。
长沙市的交通线路可以抽象成为一个 \(n\) 个点 \(n−1\) 条边的无向图,点编号为 \(1\) 到 \(n\),任意两点间均存在恰好一条路径,显然两个点之间最多也只会有一条边相连。有一个包含一些点对 \((x,y)\) 的可重集合\(S\),共价大爷的旅行路线是这样确定的:每次他会选择 \(S\) 中的某一对点 \((x,y)\),并从 \(x\) 出发沿着唯一路径到达 \(y\)。
小L是共价大爷的脑残粉,为了见到共价大爷的尊容,小L决定守在这张图的某条边上等待共价大爷的到来。为了保证一定能见到他,显然小L必须选择共价大爷一定会经过的边——也就是所有共价大爷可能选择的路径都经过的边。
现在小L想知道,如果他守在某一条边,是否一定能见到共价大爷。
然而长沙市总是不断的施工,也就是说,可能某个时刻某条边会断开,同时这个时刻一定也有某条新边会出现,且任意时刻图都满足任意两点间均存在恰好一条路径的条件。注意断开的边有可能和加入的新边连接着相同的两个端点。共价大爷的兴趣也会不断变化,所以S也会不断加入新点对或者删除原有的点对。当然,小L也有可能在任何时候向你提出守在某一条边是否一定能见到共价大爷的问题。你能回答小L的所有问题吗?
Sol
动态加删边用 \(LCT\)
考虑如何处理路径交。
一种方法是直接对链做一次覆盖。交必须满足被覆盖的次数为当前总的 \(S\) 集合大小
但是这种做法当我们删掉一条链上的边的时候 , 它必须要一个个把贡献去掉并且重新覆盖 , 显然是不行的。
我们要支持能够快速删除与当前边相关的所有路径覆盖操作的方法。
异或操作是支持快速撤销的 , 只需要再次异或一次就行了。
我们每次加入一条路径的时候给他随机一个权值 , 然后用这个权值去覆盖。
删除一条边时 , 我们能够直接知道这条边上的权值的异或和 , 用这个值重新覆盖一次新的路径即可,稍微画一下图就知道这个做法是对的了。这样做就要用 \(LCT\) 维护边权,不是那么好写。
另一种做法。
当我们询问一条边\((u,v)\)的时候 , 如果满足条件 , 必定是所有路径的一端在以 \(v\) 为根 \(u\) 的子树里 , 另一端在以 \(u\) 为根 \(v\) 的子树里 , 我们只需要判断一边就可以了。
于是还是给每一条路径随机一个权值 ,然后修改端点的权值。
于是只需要查询以\(u\)为根 \(v\) 的子树和是否和全局的一半一致即可。
code:
#include<bits/stdc++.h>
using namespace std;
template<class T>inline void init(T&x){
x=0;char ch=getchar();bool t=0;
for(;ch>'9'||ch<'0';ch=getchar()) if(ch=='-') t=1;
for(;ch>='0'&&ch<='9';ch=getchar()) x=(x<<1)+(x<<3)+(ch-48);
if(t) x=-x;return;
}
const int N=1e5+10;
int ID;
#define ls son[0]
#define rs son[1]
#define get_son(a) (a->fa->rs==a)
#define IS(a) ((a)&&((!(a->fa))||(a->fa->ls!=a&&a->fa->rs!=a)))
#define get_S(a) (a? a->S:0)
const int INF=1e9;
#define __ NULL
typedef long long ll;
struct node{
node *son[2],*fa;int val,S;bool rev;
node(){ls=rs=fa=__,S=val=rev=0;}
}T[N];
node* st[N];int top=0;
int n,m;
inline void update(node*p){if(!p) return;p->S=get_S(p->ls)^get_S(p->rs)^p->val;return;}
inline void push_down(node*p){
if(!p||!p->rev) return;
swap(p->ls,p->rs);
if(p->ls) p->ls->rev^=1;
if(p->rs) p->rs->rev^=1;
p->rev=0;
return;
}
inline void Push(node*p){top=0;
while(!IS(p)) st[++top]=p,p=p->fa;
push_down(p);while(top) push_down(st[top]),st[top--]=__;
}
inline void rotate(node*p){if(!p) return;
int k=get_son(p);node *q=p->fa,*gp=p->fa->fa;
q->son[k]=p->son[k^1];
if(p->son[k^1]) p->son[k^1]->fa=q;
if(!IS(q)) gp->son[get_son(q)]=p;
p->fa=gp;q->fa=p;p->son[k^1]=q;
return update(q);
}
inline void Splay(node*p){
if(!p) return;Push(p);
for(;!IS(p);rotate(p)) if(IS(p->fa)) continue;else (get_son(p->fa)==get_son(p)? rotate(p->fa):rotate(p));
return update(p);
}
int U[N*3],V[N*3],tot=0,val[N*3];
inline void access(node*p) {
node*pre=__;
for(;p;pre=p,p=p->fa) {Splay(p);p->val^=get_S(p->rs)^(get_S(pre));p->rs=pre;update(p);}
return;
}
inline void make_root(node*p){access(p);Splay(p);p->rev^=1;}
inline void split(node*p,node*q){make_root(p),access(q),Splay(q);};
inline void Link(node*p,node*q){split(p,q);p->fa=q;q->val^=p->S;update(q);}
inline void Cut(node*p,node*q){split(p,q);if(q->ls==p) p->fa=q->ls=__,update(q);}
int Sum=0;
int main()
{
srand(time(NULL));
init(ID);init(n),init(m);
int u,v;
for(int i=1;i<n;++i) {init(u),init(v);Link(&T[u],&T[v]);}
for(int i=1;i<=m;++i) {
int tp;
init(tp);
if(tp==1) {
int x,y;
init(x),init(y),init(u),init(v);
Cut(&T[x],&T[y]);
Link(&T[u],&T[v]);
}else if(tp==2) {
++tot;init(U[tot]),init(V[tot]);
val[tot]=(ll)rand()*rand()%INF;
Sum^=val[tot];
make_root(&T[U[tot]]);
T[U[tot]].val^=val[tot];T[U[tot]].S^=val[tot];
make_root(&T[V[tot]]);
T[V[tot]].val^=val[tot];T[V[tot]].S^=val[tot];
}else if(tp==3) {
int x;init(x);
Sum^=val[x];
make_root(&T[U[x]]);
T[U[x]].val^=val[x];T[U[x]].S^=val[x];
make_root(&T[V[x]]);
T[V[x]].val^=val[x];T[V[x]].S^=val[x];
}
else {
int x,y;init(x),init(y);
split(&T[x],&T[y]);
if(T[x].S==Sum) puts("YES");else puts("NO");
}
}
}