[SDOI2008]Cave 洞穴勘測

[SDOI2008]Cave 洞穴勘測

題目描述:

輝輝熱衷於洞穴勘測。

某天,他按照地圖來到了一片被標記爲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:

樣例輸入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

輸出樣例#1:

樣例輸出1 cave.out
No
Yes
No


樣例輸出2 cave.out
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啦。
對於加邊和刪邊cut和link函數就可以完美解決。
想一下lct的性質,因爲lct中的splay是按照決了,唯一的問題是,如何查詢連通性。
判斷根節點是否相同。但是如何查找根節點深度爲關鍵字維護的,這樣如果我們將要查找的節點access一下,我們就得到了一個含有根節點的auxiliary tree。由於根節點的深度在這顆splay中一定是最小的,那麼一直往他的左兒子上跳就可以了。

代碼:
#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;

const int max_n = 10001;
const int max_c = 11;

struct splay_node{
	int rev,num;
	splay_node *ch[2];
	splay_node *pre;
	
	void pushdown();
	void set_ch(splay_node *child,int wh);
	int get_wh();
	bool is_root();
}pool[max_n],*null;

inline void splay_node::pushdown()
{
	if(rev)
	{
		rev=0;
		swap(ch[0],ch[1]);
		ch[0]->rev^=1;
		ch[1]->rev^=1;
	}
} 

inline void splay_node::set_ch(splay_node *child,int wh)
{
	pushdown();
	ch[wh]=child;
	child->pre=this;
}

inline int splay_node::get_wh()
{
	return pre->ch[0]==this ? 0 : 1;
}

inline bool splay_node::is_root()
{
	if(pre==null) return true;
	return (pre->ch[0]!=this && pre->ch[1]!=this);
}

int n,m,x,y,tot;
char c[max_c]; 

inline splay_node *newnode()
{
	splay_node *newone;
	
	newone=pool+ ++tot;
	newone->rev=0;
	newone->ch[0]=newone->ch[1]=newone->pre=null;
	
	return newone;
}

inline void init()
{
	tot=0;
	null=pool;
	null->rev=0;
	null->ch[0]=null->ch[1]=null->pre=null;
	
	for(int i=1; i<=n; ++i) newnode()->num=i;
}

inline void rotate(splay_node *now)
{
	splay_node *fa=now->pre,*grand=now->pre->pre;
	if(grand!=null) grand->pushdown();
	fa->pushdown(); now->pushdown();
	int wh=now->get_wh();
	fa->set_ch(now->ch[wh^1],wh);
	if(fa->is_root()) now->pre=grand;
	else grand->set_ch(now,fa->get_wh());
	now->set_ch(fa,wh^1);
}

inline void splay(splay_node *now)
{
	if(now->is_root()) return;
	for(; !now->is_root(); rotate(now))
	  if(!now->pre->is_root()) 
	    now->pre->get_wh()==now->get_wh() ? rotate(now->pre) : rotate(now);
}

inline splay_node *access(splay_node *now)
{ 
	splay_node *y=null;
	
	for(; now!=null; y=now,now=now->pre)
	{
		splay(now);
		now->set_ch(y,1);
	}
	
	return y;
}

inline void changeroot(splay_node *now)
{
	access(now)->rev^=1;
	splay(now);
}

inline void link(splay_node *u,splay_node *v)
{
	changeroot(u);
	u->pre=v;
	access(u);
}

inline void cut(splay_node *u,splay_node *v)
{
	changeroot(u);
	access(v);
	splay(u);
	u->pushdown();
	u->ch[1]=v->pre=null;
}

inline int find(splay_node *now)
{
	splay_node *x=access(now);//先將要查詢的點access 
	
	while(x->ch[0]!=null) //根節點一定在以左兒子爲根的子樹上 
		x=x->ch[0];
	  
	return x->num;
}

inline bool query(splay_node *u,splay_node *v)
{
	int x1=find(u),x2=find(v);
	return x1==x2 ? 1 : 0;//如果根相等則連通 
}

int main()
{
	scanf("%d%d",&n,&m);
	init();
	
	for(int i=1; i<=m; ++i)
	{
		scanf("%s%d%d",c,&x,&y);
		
		if(c[0]=='C') link(pool+x,pool+y);
		if(c[0]=='D') cut(pool+x,pool+y);
		if(c[0]=='Q') 
		{
			if(query(pool+x,pool+y)) printf("Yes\n");
			else printf("No\n");
		}
	}
	
	return 0;
}



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章