洛谷P2147 [SDOI2008] 洞穴勘探 [LCT]
題目傳送門
洞穴勘探
題目描述
輝輝熱衷於洞穴勘測。
某天,他按照地圖來到了一片被標記為JSZX的洞穴群地區。經過初步勘測,輝輝發現這片區域由n個洞穴(分別編號為1到n)以及若幹通道組成,並且每條通道連接了恰好兩個洞穴。假如兩個洞穴可以通過一條或者多條通道按一定順序連接起來,那麽這兩個洞穴就是連通的,按順序連接在一起的這些通道則被稱之為這兩個洞穴之間的一條路徑。 洞穴都十分堅固無法破壞,然而通道不太穩定,時常因為外界影響而發生改變,比如,根據有關儀器的監測結果,123號洞穴和127號洞穴之間有時會出現一條通道,有時這條通道又會因為某種稀奇古怪的原因被毀。
輝輝有一臺監測儀器可以實時將通道的每一次改變狀況在輝輝手邊的終端機上顯示:
如果監測到洞穴u和洞穴v之間出現了一條通道,終端機上會顯示一條指令 Connect u v
如果監測到洞穴u和洞穴v之間的通道被毀,終端機上會顯示一條指令 Destroy u v
經過長期的艱苦卓絕的手工推算,輝輝發現一個奇怪的現象:無論通道怎麽改變,任意時刻任意兩個洞穴之間至多只有一條路徑。
因而,輝輝堅信這是由於某種本質規律的支配導致的。因而,輝輝更加夜以繼日地堅守在終端機之前,試圖通過通道的改變情況來研究這條本質規律。 然而,終於有一天,輝輝在堆積成山的演算紙中崩潰了……他把終端機往地面一砸(終端機也足夠堅固無法破壞),轉而求助於你,說道:“你老兄把這程序寫寫吧”。
輝輝希望能隨時通過終端機發出指令 Query u v
,向監測儀詢問此時洞穴u和洞穴v是否連通。現在你要為他編寫程序回答每一次詢問。 已知在第一條指令顯示之前,JSZX洞穴群中沒有任何通道存在。
輸入輸出格式
輸入格式:第一行為兩個正整數n和m,分別表示洞穴的個數和終端機上出現過的指令的個數。 以下m行,依次表示終端機上出現的各條指令。每行開頭是一個表示指令種類的字符串s("Connect”、”Destroy”或者”Query”,區分大小寫),之後有兩個整數u和v (1≤u, v≤n且u≠v) 分別表示兩個洞穴的編號。
對每個Query指令,輸出洞穴u和洞穴v是否互相連通:是輸出”Yes”,否則輸出”No”。(不含雙引號)
輸入輸出樣例
輸入樣例#1:200 5
Query 123 127
Connect 123 127
Query 123 127
Destroy 127 123
Query 123 127
輸出樣例#1:
No
Yes
No
輸入樣例#2:
3 5
Connect 1 2
Connect 3 1
Query 2 3
Destroy 1 3
Query 2 3
輸出樣例#2:
Yes
No
說明
數據說明
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操作以免超時
分析:
那這道題來練練LCT。
完全是LCT的板子,三種操作分別對應$link$,$cut$,$findroot$。就這樣。
感覺我還沒有把LCT理解透徹,做LCT的題目還是不夠熟練,還是得多練才行。
Code:
//It is made by HolseLee on 2nd Oct 2018 //Luogu.org P2147 #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> using namespace std; const int N=1e4+7; int n,m; struct LCT { int fa[N],ch[N][2],sign[N],q[N],top; void ready() { memset(fa,0,sizeof(fa)); memset(ch,0,sizeof(ch)); memset(sign,0,sizeof(sign)); memset(q,0,sizeof(q)); top=0; } inline void pushr(int x) { int temp=ch[x][0]; ch[x][0]=ch[x][1]; ch[x][1]=temp; sign[x]^=1; } inline void pushdown(int x) { if( !sign[x] ) return; if( ch[x][0] ) pushr(ch[x][0]); if( ch[x][1] ) pushr(ch[x][1]); sign[x]^=1; } inline bool isroot(int x) { return (ch[fa[x]][0]!=x && ch[fa[x]][1]!=x); } inline void rotate(int x) { int y=fa[x], z=fa[y]; int k=(ch[y][1]==x), w=ch[x][k^1]; if( !isroot(y) ) ch[z][ch[z][1]==y]=x; ch[x][k^1]=y; ch[y][k]=w; if( w ) fa[w]=y; fa[y]=x; fa[x]=z; } inline void splay(int x) { top=1; q[top]=x; for(int i=x; !isroot(i); i=fa[i]) q[++top]=fa[i]; while( top ) pushdown(q[top--]); while( !isroot(x) ) { int y=fa[x], z=fa[y]; if( !isroot(y) ) { (ch[z][1]==y)^(ch[y][1]==x) ? rotate(x) : rotate(y); } rotate(x); } } inline void access(int x) { for(int y=0; x; y=x, x=fa[x]) splay(x), ch[x][1]=y; } inline void makeroot(int x) { access(x); splay(x); pushr(x); } inline int findroot(int x) { access(x); splay(x); while( ch[x][0] )pushdown(x), x=ch[x][0]; splay(x); return x; } inline void link(int x,int y) { makeroot(x); if( findroot(y)!=x ) fa[x]=y; } inline void cut(int x,int y) { makeroot(x); if( findroot(y)==x && fa[y]==x && !ch[y][0] ) fa[y]=ch[x][1]=0; } }T; int main() { scanf("%d%d",&n,&m); char op[10]; int x,y; T.ready(); for(int i=1; i<=m; ++i) { scanf("%s%d%d",op,&x,&y); if( op[0]==‘C‘ ) { T.link(x,y); } else if( op[0]==‘D‘ ) { T.cut(x,y); } else { if( T.findroot(x)==T.findroot(y) ) printf("Yes\n"); else printf("No\n"); } } return 0; }
洛谷P2147 [SDOI2008] 洞穴勘探 [LCT]