【BZOJ2049】洞穴勘测(SDOI2008)-LCT真·模板题
测试地址:洞穴勘测
做法:听说这题可以用并查集水过......为了训练LCT还是不要做这种事了吧......
根据题目描述,图无论怎么变化都是一个森林,那么我们就要用到LCT最经典的用法了:维护森林的连通性。然后link和cut就是模板了,检测两个点之间的连通性的话,我们只需要检查这两个点在不在同一棵树上(真的树,不是splay)即可。至于模板我是向kuangbin神犇学的,既快,写着也舒服。
犯二的地方:splay的时候把跳出循环的条件写成!pre[x]了,导致TLE到死......一定要记住pre[x]为0的时候表示x为真树的根,rt[x]为1的时候才表示x为它所在的splay中的根。
以下是本人代码:
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
int n,m,ch[10010][2]={0},pre[10010]={0};
bool rev[10010]={0},rt[10010]={0};
void reverse(int x)
{
rev[x]^=1;
swap(ch[x][0],ch[x][1]);
}
void pushdown(int x)
{
if (rev[x])
{
reverse(ch[x][0]);
reverse(ch[x][1]);
rev[x]=0;
}
}
void pushup(int x)
{
}
void rotate(int x,bool f)
{
int y=pre[x];
ch[y][!f]=ch[x][f];
pre[ch[x][f]]=y;
ch[x][f]=y;
if (!rt[y]) ch[pre[y]][ch[pre[y]][1]==y]=x;
else rt[y]=0,rt[x]=1;
pre[x]=pre[y];pre[y]=x;
pushup(y);
}
void Push(int x)
{
if (!rt[x]) Push(pre[x]);
pushdown(x);
}
void Splay(int x)
{
Push(x);
while(!rt[x])
{
if (rt[pre[x]]) rotate(x,ch[pre[x]][0]==x);
else
{
int y=pre[x],z=pre[pre[x]];
bool f=(ch[z][1]==y);
if (ch[y][f]==x) rotate(y,!f),rotate(x,!f);
else rotate(x,f),rotate(x,!f);
}
}
pushup(x);
}
void access(int x)
{
int y=0;
do
{
Splay(x);
rt[ch[x][1]]=1,rt[ch[x][1]=y]=0;
pushup(x);
x=pre[y=x];
}while(x);
}
int find_root(int x)
{
while(pre[x]) x=pre[x];
return x;
}
void move(int x)
{
access(x);
Splay(x);
reverse(x);
}
void cut(int x,int y)
{
move(x);
Splay(y);
pre[ch[y][0]]=pre[y];
pre[y]=0;
rt[ch[y][0]]=1;
ch[y][0]=0;
pushup(y);
}
void link(int x,int y)
{
move(x);
pre[x]=y;
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) rt[i]=1;
for(int i=1;i<=m;i++)
{
char op[30];
int x,y;
scanf("%s%d%d",op,&x,&y);
if (op[0]=='Q')
{
if (find_root(x)==find_root(y)) printf("Yes\n");
else printf("No\n");
}
if (op[0]=='C') link(x,y);
if (op[0]=='D') cut(x,y);
}
return 0;
}