算法复习——LCT(bzoj2049洞穴勘测)
题目:
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操作以免超时
题解:
LCT简单模版题;
心得:
一句话总结LCT:将树剖分成几个实边后利用splay维护每一个实边,注意father既维护了splay树上除节点以外的father,也维护了根节点对应实路径的pathparent,而son的信息只是关于spaly的。
代码:
#include<iostream> #include<cstdlib> #include<cstdio> #include<cmath> #include<ctime> #include<cctype> #include<cstring> #include<string> #include<algorithm> using namespace std; const int N=2e6+5; int n,m,u,v; int son[N][2],father[N],tag[N],cnt,stack[N]; char s[15]; inline int get(int now) { return son[father[now]][1]==now; } inline void pushdown(int now) { if(tag[now]&&now) { swap(son[now][0],son[now][1]); tag[son[now][0]]^=1; tag[son[now][1]]^=1; tag[now]=0; } } inline bool isroot(int now) { if(!father[now]) return true; else if(son[father[now]][1]!=now&&son[father[now]][0]!=now) return true; else return false; } inline void rotate(int now) { pushdown(father[now]),pushdown(now); int fa=father[now],ofa=father[fa],which=get(now); if(!isroot(fa)) son[ofa][son[ofa][1]==fa]=now; son[fa][which]=son[now][which^1],father[son[fa][which]]=fa; son[now][which^1]=fa,father[fa]=now,father[now]=ofa; } inline void splay(int now) { stack[cnt=0]=now; for(int i=now;!isroot(i);i=father[i]) stack[++cnt]=father[i]; for(int i=cnt;i>=0;i--) pushdown(stack[i]); while(!isroot(now)) { if(!isroot(father[now])) rotate(get(father[now])==get(now)?father[now]:now); rotate(now); } } inline void access(int now) { int temp=0; for(;now;temp=now,now=father[now]) splay(now),son[now][1]=temp; } inline int getroot(int now) { access(now);splay(now); while(son[now][0]) now=son[now][0]; return now; } inline void makeroot(int now) { access(now);splay(now);tag[now]^=1; } inline void link(int a,int b) { makeroot(a);father[a]=b; } inline void cut(int a,int b) { makeroot(a);access(b);splay(b); father[a]=son[b][0]=0; } int main() { //freopen("a.in","r",stdin); scanf("%d%d",&n,&m); while(m--) { scanf("%s%d%d",s,&u,&v); if(s[0]=='Q') { if(getroot(u)==getroot(v)) printf("Yes\n"); else printf("No\n"); } if(s[0]=='C') link(u,v); if(s[0]=='D') cut(u,v); } return 0; }