輝輝熱衷於洞穴勘測。某天,他按照地圖來到了一片被標記爲JSZX的洞穴羣地區。經過初步勘測,輝輝發現這片區域由n個洞穴(分別編號爲1到n)以及若干通道組成,並且每條通道連接了恰好兩個洞穴。假如兩個洞穴可以通過一條或者多條通道按一定順序連接起來,那麼這兩個洞穴就是連通的,按順序連接在一起的這些通道則被稱之爲這兩個洞穴之間的一條路徑。洞穴都十分堅固無法破壞,然而通道不太穩定,時常因爲外界影響而發生改變,比如,根據有關儀器的監測結果,123號洞穴和127號洞穴之間有時會出現一條通道,有時這條通道又會因爲某種稀奇古怪的原因被毀。輝輝有一臺監測儀器可以實時將通道的每一次改變狀況在輝輝手邊的終端機上顯示:如果監測到洞穴u和洞穴v之間出現了一條通道,終端機上會顯示一條指令 Connect u v 如果監測到洞穴u和洞穴v之間的通道被毀,終端機上會顯示一條指令 Destroy u v 經過長期的艱苦卓絕的手工推算,輝輝發現一個奇怪的現象:無論通道怎麼改變,任意時刻任意兩個洞穴之間至多隻有一條路徑。因而,輝輝堅信這是由於某種本質規律的支配導致的。因而,輝輝更加夜以繼日地堅守在終端機之前,試圖通過通道的改變情況來研究這條本質規律。然而,終於有一天,輝輝在堆積成山的演算紙中崩潰了……他把終端機往地面一砸(終端機也足夠堅固無法破壞),轉而求助於你,說道:“你老兄把這程序寫寫吧”。輝輝希望能隨時通過終端機發出指令 Query u v,向監測儀詢問此時洞穴u和洞穴v是否連通。現在你要爲他編寫程序回答每一次詢問。已知在第一條指令顯示之前,JSZX洞穴羣中沒有任何通道存在。
Input第一行爲兩個正整數n和m,分別表示洞穴的個數和終端機上出現過的指令的個數。以下m行,依次表示終端機上出現的各條指令。每行開頭是一個表示指令種類的字符串s("Connect”、”Destroy”或者”Query”,區分大小寫),之後有兩個整數u和v (1≤u, v≤n且u≠v) 分別表示兩個洞穴的編號。
Output對每個Query指令,輸出洞穴u和洞穴v是否互相連通:是輸出”Yes”,否則輸出”No”。(不含雙引號)
Sample Input樣例輸入1 cave.in
200 5
Query 123 127
Connect 123 127
Query 123 127
Destroy 127 123
Query 123 127
樣例輸入2 cave.in
3 5
Connect 1 2
Connect 3 1
Query 2 3
Destroy 1 3
Query 2 3
Sample Output
樣例輸出1 cave.out
No
Yes
No
樣例輸出2 cave.out
Yes
No
Hint
數據說明
10%的數據滿足n≤1000, m≤20000
20%的數據滿足n≤2000, m≤40000
30%的數據滿足n≤3000, m≤60000
40%的數據滿足n≤4000, m≤80000
50%的數據滿足n≤5000, m≤100000
60%的數據滿足n≤6000, m≤120000
70%的數據滿足n≤7000, m≤140000
80%的數據滿足n≤8000, m≤160000
90%的數據滿足n≤9000, m≤180000
100%的數據滿足n≤10000, m≤200000
保證所有Destroy指令將摧毀的是一條存在的通道本題輸入、輸出規模比較大,建議c\c++選手使用scanf和printf進行I\O操作以免超時
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<cstring>
#include<algorithm>
#define F(x) tree[x].fa
#define LC(x) tree[x].child[0]
#define RC(x) tree[x].child[1]
#define REV(x) tree[x].rev
#define Size 100010
using namespace std;
inline int read(){
int sum=0,fg=1;char c=getchar();
while(c<'0' || c>'9'){if(c=='-')fg=-1;c=getchar();}
while(c>='0' && c<='9'){sum=sum*10+c-'0';c=getchar();}
return sum*fg;
}
struct lct{
int fa,child[2],rev;
}tree[Size];
struct link_cut_tree{
inline bool isroot(int x){
return LC(F(x))!=x && RC(F(x))!=x;
}
inline void pushdown(int x){
if(REV(x)){
REV(x)^=1;REV(LC(x))^=1;REV(RC(x))^=1;
swap(LC(x),RC(x));
}
}
void Pushdown(int x){
if(!isroot(x))Pushdown(F(x));
pushdown(x);
}
inline void rotate(int x){
int A=F(x),B=F(A);bool w=(RC(A)==x);
if(!isroot(A)){
if(LC(B)==A)LC(B)=x;
else if(RC(B)==A)RC(B)=x;
}
F(tree[x].child[w^1])=A;F(A)=x;F(x)=B;
tree[A].child[w]=tree[x].child[w^1];tree[x].child[w^1]=A;
}
inline void splay(int x){
Pushdown(x);
while(!isroot(x)){
if(!isroot(F(x)))rotate(x);
rotate(x);
}
}
inline void access(int x){
for(int i=0;x;i=x,x=F(x))splay(x),RC(x)=i;
}
inline int find_root(int x){
access(x);splay(x);
while(LC(x))x=LC(x);
return x;
}
inline void reverse(int x){
access(x);splay(x);REV(x)^=1;
}
inline void link(int x,int y){
reverse(x);F(x)=y;
}
inline void cut(int x,int y){
reverse(x);access(y);splay(y);
F(LC(y))=0;LC(y)=0;
}
inline int query(int x,int y){
reverse(x);access(y);splay(y);
while(LC(y))y=LC(y);
if(x==y)return 1;
else return 0;
}
}LCT;
int main(){
int n=read(),m=read();
char tp[10];
while(m--){
scanf("%s",tp);
int x,y;
if(tp[0]=='C'){
x=read();y=read();
LCT.link(x,y);
}
else if(tp[0]=='D'){
x=read();y=read();
LCT.cut(x,y);
}
else if(tp[0]=='Q'){
x=read();y=read();
if(LCT.query(x,y)==1)printf("Yes\n");
else printf("No\n");
}
}
return 0;
}