2049: [Sdoi2008]Cave 洞穴勘測
Time Limit: 10 Sec Memory Limit: 259 MBSubmit: 1528 Solved: 644
[ Submit][ Status]
Description
輝輝熱衷於洞穴勘測。某天,他按照地圖來到了一片被標記為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
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
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操作以免超時
Source
很簡單的動態樹操作,只有cut和link,判斷在不在同一顆子樹
1 /* *********************************************** 2 Author :kuangbin 3 Created Time :2013-9-3 22:29:16 4 File Name :F:\2013ACM練習\專題學習\動態樹-LCT\BZOJ2049洞穴勘測.cpp 5 ************************************************ */ 6 7 #include <stdio.h> 8 #include <string.h> 9 #include <iostream> 10 #include <algorithm> 11 #include <vector> 12 #include <queue> 13 #include <set> 14 #include <map> 15 #include <string> 16 #include <math.h> 17 #include <stdlib.h> 18 #include <time.h> 19 using namespace std; 20 21 //支持三種操作: 22 //1.合並子樹 23 //2.分裂子樹 24 //3.詢問x是否可達y 25 // 26 //Access(x) : 其實是把路徑x到根打通,並且整條鏈用splay維護,這顆splay的 27 //所有節點就是這條路徑上的節點 28 //link(u,v) : 把兩顆子樹合並為一顆樹。先把u置為根,然后把u的父親置為v. 29 //cut(u,v) : 把u和v分離成兩顆子樹。先把u置為根,然后把y splay到根,把 30 //y 把左兒子與y分離 31 const int MAXN = 10010; 32 int ch[MAXN][2],pre[MAXN]; 33 int rev[MAXN]; 34 bool rt[MAXN];//rt[]表示該點是否為splay的根 35 void Update_Rev(int r) 36 { 37 if(!r)return; 38 swap(ch[r][0],ch[r][1]); 39 rev[r] ^= 1; 40 } 41 void push_down(int r) 42 { 43 if(rev[r]) 44 { 45 Update_Rev(ch[r][0]); 46 Update_Rev(ch[r][1]); 47 rev[r] = 0; 48 } 49 } 50 void push_up(int r) 51 { 52 53 } 54 void Rotate(int x) 55 { 56 int y = pre[x], kind = ch[y][1]==x; 57 ch[y][kind] = ch[x][!kind]; 58 pre[ch[y][kind]] = y; 59 pre[x] = pre[y]; 60 pre[y] = x; 61 ch[x][!kind] = y; 62 if(rt[y]) 63 rt[y] = false, rt[x] = true; 64 else 65 ch[pre[x]][ch[pre[x]][1]==y] = x; 66 push_up(y); 67 } 68 void P(int r) 69 { 70 if(!rt[r])P(pre[r]); 71 push_down(r); 72 } 73 void Splay(int r) 74 { 75 P(r); 76 while( !rt[r] ) 77 { 78 int f = pre[r], ff = pre[f]; 79 if(rt[f]) 80 Rotate(r); 81 else if( (ch[ff][1]==f)==(ch[f][1]==f) ) 82 Rotate(f), Rotate(r); 83 else 84 Rotate(r), Rotate(r); 85 } 86 push_up(r); 87 } 88 //Access(x) :打通路徑x到根節點 89 int Access(int x) 90 { 91 int y = 0; 92 do 93 { 94 Splay(x); 95 rt[ch[x][1]] = true, rt[ch[x][1]=y] = false; 96 push_up(x); 97 x = pre[y=x]; 98 } 99 while(x); 100 return y; 101 } 102 //判斷是否同根(真實的樹,非splay) 103 bool judge(int u,int v) 104 { 105 while(pre[u]) u = pre[u]; 106 while(pre[v]) v = pre[v]; 107 return u == v; 108 } 109 //使r成為所在樹的根 110 void mroot(int r) 111 { 112 Access(r); 113 Splay(r); 114 Update_Rev(r); 115 } 116 void link(int u,int v) 117 { 118 if(judge(u,v)) 119 { 120 puts("-1"); 121 return; 122 } 123 mroot(u); 124 pre[u] = v; 125 } 126 void cut(int u,int v) 127 { 128 if(u == v || !judge(u,v)) 129 { 130 puts("-1"); 131 return; 132 } 133 mroot(u); 134 Splay(v); 135 pre[ch[v][0]] = pre[v]; 136 pre[v] = 0; 137 rt[ch[v][0]] = true; 138 ch[v][0] = 0; 139 push_up(v); 140 } 141 int main() 142 { 143 //freopen("in.txt","r",stdin); 144 //freopen("out.txt","w",stdout); 145 int n,m; 146 while(scanf("%d%d",&n,&m) == 2) 147 { 148 for(int i = 0;i <= n;i++) 149 { 150 pre[i] = 0; 151 ch[i][0] = ch[i][1] = 0; 152 rev[i] = 0; 153 rt[i] = true; 154 } 155 char op[20]; 156 int u,v; 157 while(m--) 158 { 159 scanf("%s%d%d",op,&u,&v); 160 if(op[0] == 'Q') 161 { 162 if(judge(u,v))printf("Yes\n"); 163 else printf("No\n"); 164 } 165 else if(op[0] == 'C') 166 link(u,v); 167 else cut(u,v); 168 } 169 } 170 return 0; 171 }