BZOJ 2049:[Sdoi2008]Cave 洞穴勘测
辉辉有一台监测仪器可以实时将通道的每一次改变状况在辉辉手边的终端机上显示:如果监测到洞穴u和洞穴v之间出现了一条通道,终端机上会显示一条指令 Connect u v 。如果监测到洞穴u和洞穴v之间的通道被毁,终端机上会显示一条指令 Destroy u v 。经过长期的艰苦卓绝的手工推算,辉辉发现一个奇怪的现象:无论通道怎么改变,任意时刻任意两个洞穴之间至多只有一条路径。因而,辉辉坚信这是由于某种本质规律的支配导致的。因而,辉辉更加夜以继日地坚守在终端机之前,试图通过通道的改变情况来研究这条本质规律。然而,终于有一天,辉辉在堆积成山的演算纸中崩溃了……他把终端机往地面一砸(终端机也足够坚固无法破坏),转而求助于你,说道:“你老兄把这程序写写吧”。辉辉希望能随时通过终端机发出指令 Query u v,向监测仪询问此时洞穴u和洞穴v是否连通。现在你要为他编写程序回答每一次询问。已知在第一条指令显示之前,JSZX洞穴群中没有任何通道存在。
Input
第一行为两个正整数n(10000)和m(200000),分别表示洞穴的个数和终端机上出现过的指令的个数。以下m行,依次表示终端机上出现的各条指令。每行开头是一个表示指令种类的字符串s("Connect”、”Destroy”或者”Query”,区分大小写),之后有两个整数u和v (1≤u, v≤n且u≠v) 分别表示两个洞穴的编号。
Output
对每个Query指令,输出洞穴u和洞穴v是否互相连通:是输出”Yes”,否则输出”No”。(不含双引号)
Sample Input | Sample Output |
200 5 Query 123 127 Connect 123 127 Query 123 127 Destroy 127 123 Query 123 127 3 5 Connect 1 2 Connect 3 1 Query 2 3 Destroy 1 3 Query 2 3 | No Yes No
|
Yes No
|
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
#define N 10010
struct Node {
Node *ch[2],*fa;
int rev,value;
Node(){
ch[0] = ch[1] = fa = NULL;
rev = 0;
}
Node(int v){
ch[0] = ch[1] = fa = NULL;
rev = 0; value = v;
}
int lr_ch(Node *(&node)) {
this -> push_down();
return ch[1] == node;
}
void link_ch(Node *(&node),int d) {
ch[d] = node;
if(node!=NULL) node -> fa = this;
}
void push_rev(Node *(&node)) {
swap(node->ch[0],node->ch[1]);
node->rev ^= 1;
}
void push_down() {
if(rev == 0) return;
if(ch[0] != NULL) push_rev(ch[0]);
if(ch[1] != NULL) push_rev(ch[1]);
rev = 0;
}
}*root,*nod[N];
inline int is_Root(Node *(&node));
// 一定要注意splay之前 小棵splay树上根node之间的节点下放
void push_Path(Node *(&node)){
Node *stack[N],*now = node;
int top = 0;
while(!is_Root(now)){
stack[++top] = now;
now = now -> fa;
}
stack[++top] = now;
while(top != 0) stack[top--] -> push_down();
}
//单旋操作,注意与splay模板的区别
void rotate(Node *(&node),int d) {
Node *ff = node -> fa -> fa;
Node *f = node -> fa;
f -> link_ch(node->ch[d],d^1);
//当ff为"天花板"时,"天花板"是没有孩子的
if(!is_Root(f)) ff->link_ch(node,ff->lr_ch(f));
else node->fa = ff;
node -> link_ch(f,d);
}
//注意splay的写法,别用函数递归,防止爆栈
void splay(Node *(&node)){
push_Path(node);
while(!is_Root(node)){
node -> fa -> push_down(); node -> push_down();
if(is_Root(node->fa)){
rotate(node,node->fa->ch[0]==node);
return;
}
int b1 = (node->fa->fa->ch[0]==node->fa);
int b2 = (node->fa->ch[0]==node);
if(b1==b2) rotate(node->fa,b2);
else rotate(node,b2);
rotate(node,b1);
}
}
//判断node节点是否为一小棵splay树的根
inline int is_Root(Node *(&node)) {
if(node->fa->ch[0]==node) return 0;
if(node->fa->ch[1]==node) return 0;
return 1;
}
//access操作,打通node节点与"天花板"root
void access(Node *(&node)) {
Node *up = node,*down = NULL;
while(up != root) {
splay(up);
up -> push_down();
up -> ch[1] = down;
down = up; up = up -> fa;
}
}
//将大森林的树根换成node!
void re_Root(Node *(&node)) {
access(node);
splay(node);
node -> push_rev(node);
}
//要记住: link操作的前提是link后 不能形成环
void link(Node *(&node1),Node *(&node2)) {
re_Root(node1);
node1 -> fa = node2;
access(node1);
}
//要记住: cut操作node1必须与node2有边存在
void cut(Node *(&node1),Node *(&node2)) {
re_Root(node2);
access(node1);
splay(node1);
node2 -> fa = root;
node1 -> push_down();
node1 -> ch[0] = NULL;
}
//判断node1余node2是否在同一棵小splay树中
int judge(Node *node1,Node *node2){
while(node1->fa!=root) node1 = node1 -> fa;
while(node2->fa!=root) node2 = node2 -> fa;
return node1==node2;
}
//初始化所有节点与"天花板"
void init(int n) {
root = new Node(-1);
for(int i=1;i<=n;i++) {
nod[i] = NULL;
nod[i] = new Node(i);
nod[i] -> fa = root;
}
}
//释放节点所占空间;但是注意nod[i]节点没有被初始化
void delet(Node *(&node)){
if(node==NULL) return;
delet(node->ch[0]);
delet(node->ch[1]);
delete node; node = NULL;
}
int main() {
int n,m,x,y;
char s[25];
while(~scanf("%d%d",&n,&m)){
init(n);
for(int i = 1;i<=m;i++){
scanf("%s%d%d",s,&x,&y);
if(s[0]=='C') link(nod[x],nod[y]);
if(s[0]=='D') cut(nod[x],nod[y]);
if(s[0]=='Q') printf("%s\n",judge(nod[x],nod[y])?"Yes":"No");
}
delet(root);
}
return 0;
}