BZOJ 2049: [Sdoi2008]Cave 洞穴勘測 (動態樹入門)


2049: [Sdoi2008]Cave 洞穴勘測

Time Limit: 10 Sec   Memory Limit: 259 MB
Submit: 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

樣例輸入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操作以免超時

 

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 }

 

 

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM