题目大意:
给定一棵树和一些路径的集合,同时树和集合都可以支持在线修改,然后在某一时刻求一条边是否被所有集合之中的路径给覆盖。
思路:
考虑一个简单的思路,每当添加一条路径,我们就把在这条路径上的所有边的边权都异或上一个随机值,然后对于任意一条需要询问的边,我们只需要判断它的权值是否等于目前所有的路径的权值的异或和即可。
当我们的权值很大的时候,出错的概率很低,所以可以近似为正确的。
但是树的形态也需要动态修改,这就说明一条路径在不同的版本中,它所代表的边是不一样的,这就很麻烦。
但是仔细观察一下就可以发现,新加的边一定会在树上形成一个环,然后所有在环中经过那条需要删除的边的路径都会绕道,也就是在环上走原来的路径的补集,这样的话,我们只要将整个环异或上这个删除的边的边权,这样便正好达到了取补集的效果。
于是只需要用lct维护一下边权和即可。
/*=======================================
* Author : ylsoi
* Time : 2019.1.28
* Problem : uoj207
* E-mail : ylsoi@foxmail.com
* ====================================*/
#include<bits/stdc++.h>
#define REP(i,a,b) for(int i=a,i##_end_=b;i<=i##_end_;++i)
#define DREP(i,a,b) for(int i=a,i##_end_=b;i>=i##_end_;--i)
#define debug(x) cout<<#x<<"="<<x<<" "
#define pii pair<int,int>
#define fi first
#define se second
#define mk make_pair
#define pb push_back
typedef long long ll;
using namespace std;
void File(){
freopen("uoj207.in","r",stdin);
freopen("uoj207.out","w",stdout);
}
template<typename T>void read(T &_){
_=0; T fl=1; char ch=getchar();
for(;!isdigit(ch);ch=getchar())if(ch=='-')fl=-1;
for(;isdigit(ch);ch=getchar())_=(_<<1)+(_<<3)+(ch^'0');
_*=fl;
}
const int maxn=3e5+10;
const int mod=2e9;
int task_id,n,m;
struct Link_Cut_Tree{
#define rel(o) (ch[fa[o]][1]==o)
#define lc ch[o][0]
#define rc ch[o][1]
#define isrt(o) (ch[fa[o]][0]!=o && ch[fa[o]][1]!=o)
int ch[maxn][2],fa[maxn],w[maxn],laz[maxn];
int st[maxn];
bool tag[maxn];
void pushdown(int o){
if(tag[o]){
swap(lc,rc);
tag[lc]^=1,tag[rc]^=1,tag[o]=0;
}
if(laz[o]){
w[lc]^=laz[o],laz[lc]^=laz[o];
w[rc]^=laz[o],laz[rc]^=laz[o];
laz[o]=0;
}
}
void rotate(int o){
int f=fa[o],r=rel(o);
fa[o]=fa[f]; if(!isrt(f))ch[fa[f]][rel(f)]=o;
fa[ch[o][r^1]]=f; ch[f][r]=ch[o][r^1];
fa[f]=o; ch[o][r^1]=f;
}
void splay(int o){
int cnt=0,p=o;
while(1){
st[++cnt]=p;
if(isrt(p))break;
p=fa[p];
}
DREP(i,cnt,1)pushdown(st[i]);
for(int f;!isrt(o);rotate(o)){
f=fa[o];
if(!isrt(f))rotate(rel(f)==rel(o) ? f : o);
}
}
void access(int o){
for(int res=0;o;){
splay(o),rc=res;
res=o,o=fa[o];
}
}
void mkrt(int o){
access(o),splay(o),tag[o]^=1;
}
void split(int x,int y){
mkrt(x),access(y),splay(y);
}
void link(int x,int y){
mkrt(x),fa[x]=y;
}
void cut(int x,int y){
split(x,y);
ch[y][0]=fa[x]=0;
}
void update(int x,int y,int z){
split(x,y),w[y]^=z,laz[y]^=z;
}
}T;
int cnte,sum,w[maxn],tot;
pii node[maxn];
set<pii>G[maxn];
set<pii>::iterator it;
int e(int x,int y){
it=G[x].lower_bound(mk(y,0));
return it->se;
}
int main(){
File();
srand((unsigned)time(NULL));
read(task_id);
read(n),read(m);
cnte=n;
int u,v;
REP(i,2,n){
++cnte;
read(u),read(v);
G[u].insert(mk(v,cnte));
G[v].insert(mk(u,cnte));
T.link(u,cnte);
T.link(v,cnte);
}
int ty,x,y;
REP(i,1,m){
read(ty);
if(ty==1){
read(x),read(y),read(u),read(v);
int id=e(x,y);
T.splay(id);
int t=T.w[id];
T.w[id]=0;
T.cut(x,id),G[x].erase(mk(y,id));
T.cut(id,y),G[y].erase(mk(x,id));
T.link(u,id),G[u].insert(mk(v,id));
T.link(v,id),G[v].insert(mk(u,id));
T.update(x,y,t);
}
else if(ty==2){
read(x),read(y);
++tot;
w[tot]=1ll*rand()*rand()%mod;
node[tot]=mk(x,y);
sum^=w[tot];
T.update(x,y,w[tot]);
}
else if(ty==3){
read(x);
sum^=w[x];
T.update(node[x].fi,node[x].se,w[x]);
}
else if(ty==4){
read(x),read(y);
int id=e(x,y);
T.splay(id);
printf("%s
",T.w[id]==sum ? "YES" : "NO");
}
}
return 0;
}