[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 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 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,然后就只要根据题目要求做link和cut操作就好。
1 //Never forget why you start 2 #include<iostream> 3 #include<cstdio> 4 #include<cstdlib> 5 #include<cstring> 6 #include<cmath> 7 #include<algorithm> 8 #define ll(x) lct[x].child[0] 9 #define rr(x) lct[x].child[1] 10 #define son(x,t) lct[x].child[t] 11 using namespace std; 12 int n,m; 13 struct LCT{ 14 int child[2],fa,rev; 15 bool is_root; 16 }lct[10005]; 17 void push_rev(int root){ 18 if(!root)return; 19 swap(ll(root),rr(root)); 20 lct[root].rev^=1; 21 } 22 void push_down(int root){ 23 if(lct[root].rev){ 24 push_rev(ll(root)); 25 push_rev(rr(root)); 26 lct[root].rev^=1; 27 } 28 } 29 int getson(int root){ 30 return root==son(lct[root].fa,1); 31 } 32 void rotate(int x){ 33 if(lct[x].is_root)return; 34 int fa=lct[x].fa,t=getson(x),fafa=lct[fa].fa; 35 son(fa,t)=son(x,!t);if(son(x,!t))lct[son(x,!t)].fa=fa; 36 lct[fa].fa=x;son(x,!t)=fa; 37 lct[x].fa=fafa; 38 if(!lct[fa].is_root)son(fafa,son(fafa,1)==fa)=x; 39 else lct[x].is_root=1,lct[fa].is_root=0; 40 } 41 void push(int root){ 42 if(!lct[root].is_root)push(lct[root].fa); 43 push_down(root); 44 } 45 void splay(int x){ 46 push(x); 47 for(int fa;!lct[x].is_root;rotate(x)) 48 if(!lct[fa=lct[x].fa].is_root) 49 rotate(getson(fa)==getson(x)?fa:x); 50 } 51 void access(int x){ 52 int y=0; 53 do{ 54 splay(x); 55 lct[rr(x)].is_root=1; 56 lct[rr(x)=y].is_root=0; 57 x=lct[y=x].fa; 58 }while(x); 59 } 60 void mroot(int x){ 61 access(x); 62 splay(x); 63 push_rev(x); 64 } 65 void link(int u,int v){ 66 mroot(u); 67 lct[u].fa=v; 68 } 69 void cut(int u,int v){ 70 mroot(u); 71 access(v);splay(v); 72 push_down(v); 73 lct[son(v,0)].fa=lct[v].fa; 74 lct[son(v,0)].is_root=1; 75 son(v,0)=lct[v].fa=0; 76 } 77 bool judge(int u,int v){ 78 while(lct[u].fa)u=lct[u].fa; 79 while(lct[v].fa)v=lct[v].fa; 80 return u==v; 81 } 82 int main(){ 83 int i,j; 84 scanf("%d%d",&n,&m); 85 for(i=1;i<=n;i++){ 86 lct[i].child[0]=lct[i].child[1]=lct[i].fa=0; 87 lct[i].is_root=1; 88 } 89 for(i=1;i<=m;i++){ 90 char s[10];int u,v; 91 scanf("%s%d%d",s,&u,&v); 92 if(s[0]=='Q') 93 if(judge(u,v))printf("Yes\n"); 94 else printf("No\n"); 95 if(s[0]=='C')link(u,v); 96 if(s[0]=='D')cut(u,v); 97 } 98 return 0; 99 }