题目背景
在一个叫做Travian的世界里,生活着各个大大小小的部落。其中最为强大的是罗马、高卢和日耳曼。他们之间为了争夺资源和土地,进行了无数次的战斗。期间诞生了众多家喻户晓的英雄人物,也留下了许多可歌可泣的动人故事。

其中,在大大小小的部落之间,会有一些道路相连,这些道路是Travian世界里的重要枢纽,简单起见,你可以把这些部落与部落之间相连的道路看作一颗树,可见每条道路对于Travian世界的重要程度。有了这些道路,建筑工人就可以通过这些道路进行友好外交啦。

然而,事情并不会像想象的那样美好,由于资源的匮乏,相邻的部落(由一条道路相连的部落)之间经常会发生大大小小的冲突事件,更有甚者,会升级为部落之间的大型战争。

为了避免误伤,每当两个相邻的部落之间发生大型战争之时,这两个部落间的道路是不允许通行的,对于一些强大的部落,甚至能与多个相邻的部落同时开战,同样的,这些战争地带的道路十分危险,是不可通行的。

天下之势,分久必合,当两个部落经历了不打不相识的苦战之后,他们可以签订停战协议(暂时停战,以后依旧可能再次开战),这样,两个部落之间的道路又会重新恢复为可通行状态,建筑工人们又可以经过此地购买最新的大本营设计图纸来强大自己的部落了。

为了简单起见,我们把各大战争事件按发起的时间顺序依次编号(最先发起的战争编号就为 1,第二次战争编号就为 2,以此类推),当两个部落停战之时,则会直接告诉你这场战争的编号,然后这场战争就载入了史册,不复存在了,当然,这并不会影响到其他战争的编号。

建筑工人十分讨厌战争,因为战争,想从一个部落到另一个部落进行友好交流的建筑工人可能就此白跑一趟。所以,在他们出发之前,都会向你问问能不能到达他们想去的部落。

题目描述
简单起见,你就是要处理下面三件事,所有的事件都是按照时间顺序给出的。

1.(QQ pp qq)从第 pp 个部落出发的建筑工人想知道能否到达第 qq 个部落了,你要回答的便是(Yes/No),注意大小写

2.**(CC pp qq)**第 pp 个部落与第 qq 个部落开战了,保证他们一定是相邻的部落,且目前处于停战(未开战)状态

3.(UU xx ) 第 xx 次发生的战争结束了,它将永远的被载入史册,不复存在(保证这个消息不会告诉你多次)

输入格式
第一行两个数 nn 和 mm, nn 代表了一共有 nn 个部落,mm 代表了以上三种事件发生的总数

接下来的 n - 1n−1 行,每行两个数 pp , qq,代表了第 pp 个部落与第 qq 个部落之间有一条道路相连

接下来的 mm 行,每行表示一件事,详见题目描述

输出格式
每行一个“YesYes”或者“NoNo”,表示从第 pp 个部落出发的建筑工人能否到达第 qq 个部落


可撤销的并查集?没这么麻烦。

直接上LCT,维护连通性,每次开战时,把战斗双方用个vector存一下即可。

然后就AC了


AC代码:

#pragma GCC optimize(2)
#include<bits/stdc++.h>
//#define int long long 
using namespace std;
const int N=3e5+10;
int n,m;
vector<pair<int,int> > v;
struct Link_Cut_Tree{
	int cnt,st[N];
	struct node{int ch[2],fa,re;}t[N];
	inline void push_re(int p){swap(t[p].ch[0],t[p].ch[1]); t[p].re^=1;}
	inline void push_down(int p){
		if(t[p].re){
			if(t[p].ch[0])	push_re(t[p].ch[0]);
			if(t[p].ch[1])	push_re(t[p].ch[1]);	t[p].re^=1;
		}
	}
	inline bool isroot(int x){return t[t[x].fa].ch[0]!=x&&t[t[x].fa].ch[1]!=x;}
	inline void rotate(int x){
		int y=t[x].fa,z=t[y].fa,k=t[y].ch[1]==x,w=t[x].ch[!k];
		if(!isroot(y))	t[z].ch[t[z].ch[1]==y]=x; t[x].ch[!k]=y; t[y].ch[k]=w;
		if(w)	t[w].fa=y; t[y].fa=x; t[x].fa=z; 
	}
	inline void splay(int x){
		cnt=1; st[cnt]=x; int y=x;
		while(!isroot(y))	st[++cnt]=y=t[y].fa;
		while(cnt)	push_down(st[cnt--]);
		while(!isroot(x)){
			int y=t[x].fa,z=t[y].fa;
			if(!isroot(y))	rotate((t[y].ch[0]==x)^(t[z].ch[0]==y)?x:y); rotate(x);
		}
	}
	inline void access(int x){
		for(int y=0;x;x=t[y=x].fa) splay(x),t[x].ch[1]=y;
	}
	inline void makeroot(int x){access(x); splay(x); push_re(x);}
	inline int find(int x){
		access(x); splay(x); while(t[x].ch[0]) push_down(x),x=t[x].ch[0];
		splay(x); return x;
	}
	inline void split(int x,int y){makeroot(x); access(y); splay(y);}
	inline void link(int x,int y){makeroot(x); if(find(y)!=x) t[x].fa=y;}
	inline void cut(int x,int y){
		makeroot(x);
		if(find(y)==x&&t[y].fa==x&&!t[y].ch[0])	t[y].fa=t[x].ch[1]=0;
	}
}tr;
signed main(){
	cin>>n>>m;
	for(int i=1,a,b;i<n;i++)	scanf("%d %d",&a,&b),tr.link(a,b);
	char op[2]; int x,y;
	while(m--){
		scanf("%s %d",op,&x);
		if(op[0]=='Q')	scanf("%d",&y),puts(tr.find(x)==tr.find(y)?"Yes":"No");
		else if(op[0]=='C')	scanf("%d",&y),tr.cut(x,y),v.push_back({x,y});
		else	tr.link(v[x-1].first,v[x-1].second);
	}
	return 0;
}