1. 程式人生 > >[SDOI2008]洞穴勘測——[LCT]

[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”。(不含雙引號)

Sample  InputSample\;Input

200 5 Query 123 127 Connect 123 127 Query 123 127 Destroy 127 123 Query 123 127

Sample  OutputSample\;Output

No Yes No

【題意分析】 大概瀏覽一下題意,很明顯是維護一個森林,那麼就是赤果果♂的lct了。

connectconnectdestroydestroy分別對應LinkLinkCutCutqueryquery就兩次findrootfindroot判連通性。

因為只需要連線點的資訊,其他玩意兒都不用,所以pushuppushup就直接免了,因為這樣所以貌似並查集也能搞。。。

實測20min寫完

Code:

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cctype>
#include<cstring>
#include<vector>
#include<algorithm>
#define Read(a) a = SlowRead <int> ()
#define MAXN 300000
using namespace std;

int son[MAXN][2],father[MAXN],lazy[MAXN],stack[MAXN],n,q;

//慢讀
template <typename T> inline T SlowRead (){
	register int s = 0,w = 1;
	register char ch = getchar ();
	while (!isdigit (ch)){if (ch == '-')w = -1;ch = getchar ();}
	while (isdigit (ch)){s = (s << 3)+(s << 1)+ch-'0';ch = getchar ();}
	return s*w;
}

namespace Link_Cut_Tree{

	//是否為根
	inline bool isroot (int x){
		return ! (son[father[x]][0] == x || son[father[x]][1] == x);
	}
	
	//旋轉標記下傳
	inline void pushdown (int x){
		if (lazy[x]){
			swap (son[x][0],son[x][1]);
			lazy[son[x][0]] ^= 1;
			lazy[son[x][1]] ^= 1;
			lazy[x] = 0;
		}
	}
	
	//旋轉
	inline void rotate (int x){
		int y = father[x],z = father[y];
		int k = son[y][1] == x,kk = son[z][1] == y;
		if (!isroot (y))son[z][kk] = x;
		father[x] = z;
		son[y][k] = son[x][k^1];
		father[son[x][k^1]] = y;
		son[x][k^1] = y;
		father[y] = x;
	}
	
	//伸展
	inline void splay (int x){
		int top = 0;
		stack[++top] = x;
		for (register int i = x;!isroot (i);i = father[i])
			stack[++top] = father[i];
		for (register int i = top;i;i--)
			pushdown (stack[i]);
		while (!isroot (x)){
			int y = father[x],z = father[y];
			if (!isroot (y))
				(son[y][1] == x) ^ (son[z][1] == y)
					? rotate (x) : rotate (y);
			rotate (x);
		}
	}
	
	//走通一條路
	inline void access (int x){
		for (register int y = 0;x;y = x,x = father[x]){
			splay (x);
			son[x][1] = y;
		}
	}

	//換根
	inline void makeroot (int x){
		access (x);
		splay (x);
		lazy[x] ^= 1;
	}

	//找根
	inline int findroot (int x){
		access (x);
		splay (x);
		while (son[x][0]){
			pushdown (son[x][0]);
			x = son[x][0];
		}
		return x;
	}

	//連
	inline void link (int x,int y){
		makeroot (x);
		father[x] = y;
	}

	//切
	inline void cut (int x,int y){
		makeroot (x);
		access (y);
		splay (y);
		son[y][0] = father[x] = 0;
	}

	//判斷是不是在同一棵樹裡
	inline void query (int x,int y){
		(findroot (x) == findroot (y))
			? puts ("Yes") : puts ("No");
	}

}using namespace Link_Cut_Tree;

int main (){
	Read (n),Read (q);
	while (q--){
		char s[15];
		int x,y;
		scanf ("%s",s);
		Read (x),Read (y);
		switch (s[0]){
			case 'C' : link (x,y); break;
			case 'D' : cut (x,y); break;
			case 'Q' : query (x,y); break;
		}
	}
	return 0;
}