第一次写LCT,先来两道模板题,因为上个月被三道区间维护的超级工业的splay搞傻了,吸取了很多教训(见前几篇博客),所以LCT写得很顺啊,这里要感谢hzwer的题解。
BZOJ 2049:给出一颗树,每次加一条边或者删除一条边,询问两点的连通性。
这是一眼题,最裸的,纯天然的LCT,如果你不会先去看论文吧。半年以前看LCT感觉是一个非常高端的东西,当时反正看不懂,而TTY又有LCT爷的称号,写LCT都不用调,自然是伏地%。今天写了写发现还好。
废话不多说,查连通性就判断两个点的树的根是不是相等就可以了。
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define X first
#define Y second
#define DB double
#define MP make_pair
#define LL long long
#define pb push_back
#define sqr(_) ((_)*(_))
#define INF 0x3f3f3f3f
#define pii pair
#define pdd pair
#define ull unsigned LL
#define DEBUG(...) fprintf(stderr,__VA_ARGS__)
using namespace std;
void Read(int& x)
{
x=0;int flag=0;char c;
while(c=getchar())
{
if(c>='0'&&c<='9')x*=10,x+=c-'0',flag=1;
else if(flag)break;
}
}
const int MAXN=10010;
int n,m;
char s[10];
struct Link_Cut_Tree{
int fa[MAXN],son[MAXN][2],rev[MAXN],top,st[MAXN];
Link_Cut_Tree(){
memset(fa,0,sizeof(fa));
memset(son,0,sizeof(son));
memset(rev,0,sizeof(rev));
top=0;
}
bool isroot(int x)
{
return son[fa[x]][0]!=x&&son[fa[x]][1]!=x;
}
void rotate(int x)
{
int y=fa[x],d=(x==son[y][1]);
if(fa[y]&&!isroot(y))
{
son[fa[y]][son[fa[y]][1]==y]=x;
}
assert(x!=fa[y]);
fa[x]=fa[y];
assert(y!=x);
fa[y]=x;
son[y][d]=son[x][d^1];
if(son[x][d^1])fa[son[x][d^1]]=y;
son[x][d^1]=y;
}
void update_rev(int now)
{
rev[now]^=1;
swap(son[now][0],son[now][1]);
}
void push_down(int x)
{
if(rev[x])
{
rev[x]^=1;
update_rev(son[x][0]);
update_rev(son[x][1]);
}
}
void splay(int x)
{
st[++top]=x;
int tmp=x;
while(!isroot(tmp))
{
st[++top]=fa[tmp];
tmp=fa[tmp];
}
while(top)
{
push_down(st[top]);
top--;
}
while(!isroot(x))
{
int y=fa[x];
if(isroot(y))
{
rotate(x);
break;
}
rotate(x);
}
}
void aksess(int x)
{
int tmp=0;
while(x)
{
splay(x);
son[x][1]=tmp;
tmp=x;
x=fa[x];
}
}
void beroot(int x)
{
aksess(x);
splay(x);
update_rev(x);
}
void link(int x,int y)
{
beroot(x);
fa[x]=y;
splay(x);
}
void cut(int x,int y)
{
beroot(x);
aksess(y);
splay(y);
son[y][0]=fa[x]=0;
}
int find(int x)
{
aksess(x);
splay(x);
int tmp=x;
while(tmp)
{
if(!son[tmp][0])
return tmp;
tmp=son[tmp][0];
}
}
}LCT;
int main()
{
#ifndef ONLINE_JUDGE
freopen("cave.in","r",stdin);
freopen("cave.out","w",stdout);
#endif
Read(n);Read(m);
for(int i=1;i<=m;i++)
{
scanf("%s",s);
if(s[0]=='Q')
{
int a,b;
Read(a);Read(b);
if(LCT.find(a)==LCT.find(b))
puts("Yes");
else
puts("No");
}
else if(s[0]=='D')
{
int a,b;
Read(a);Read(b);
LCT.cut(a,b);
}
else
{
int a,b;
Read(a);Read(b);
LCT.link(a,b);
}
}
}
BZOJ 2157:给出一棵树,每次可以更改边权,路径边权置负,查询路径权值和,路径权值最大值,路径权值最小值。
虽然这一题既没有加边也没有删边,但是为了多A一道题而且能练习一下打改值标记的splay,就没有写树链剖分了。
LCT是不能维护边的权值的,我们考虑把边建成点,第i号边就是第n+i号点,然后也是模板题了。这里树上路径问题与区间维护问题有一点点不同,区间维护的splay要设两个虚点,取出区间就是把一个点splay到根,另一个点splay到根的右儿子,取出右儿子的子树。而LCT直接换根,aksess,再splay就可以了。
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define X first
#define Y second
#define DB double
#define MP make_pair
#define LL long long
#define pb push_back
#define lc son[now][0]
#define rc son[now][1]
#define sqr(_) ((_)*(_))
#define INF 0x3f3f3f3f
#define pii pair
#define pdd pair
#define ull unsigned LL
#define DEBUG(...) fprintf(stderr,__VA_ARGS__)
using namespace std;
void Read(int& x)
{
x=0;int flag=0,sgn=1;char c;
while(c=getchar())
{
if(c=='-')sgn=-1;
else if(c>='0'&&c<='9')x*=10,x+=c-'0',flag=1;
else if(flag)break;
}
x*=sgn;
}
const int MAXN=200011;
char s[10];
int n,m,f[MAXN];
struct Link_Cut_Tree{
int fa[MAXN],son[MAXN][2],st[MAXN],top,mx[MAXN],sum[MAXN],mn[MAXN],v[MAXN];
bool rev[MAXN],neg[MAXN];
Link_Cut_Tree(){
top=0;
memset(rev,0,sizeof(rev)); memset(neg,0,sizeof(neg));
memset(fa,0,sizeof(fa)); memset(son,0,sizeof(son));
memset(mx,-INF,sizeof(mx));memset(mn,INF,sizeof(mn));
memset(sum,0,sizeof(sum)); memset(v,0,sizeof(v));
}
void update_rev(int now)
{
rev[now]^=1;
swap(lc,rc);
}
void update_neg(int now)
{
neg[now]^=1;
sum[now]*=-1;
swap(mx[now],mn[now]);
mx[now]*=-1;
mn[now]*=-1;
v[now]*=-1;
}
void update(int now)
{
sum[now]=sum[lc]+sum[rc]+v[now];
mn[now]=min(mn[lc],mn[rc]);
mx[now]=max(mx[rc],mx[lc]);
if(now>n)
{
mn[now]=min(mn[now],v[now]);
mx[now]=max(mx[now],v[now]);
}
}
void push_down(int now)
{
if(rev[now])
{
rev[now]^=1;
update_rev(lc);
update_rev(rc);
}
if(neg[now])
{
neg[now]^=1;
update_neg(lc);
update_neg(rc);
}
}
bool isroot(int x)
{
return son[fa[x]][0]!=x&&son[fa[x]][1]!=x;
}
void rotate(int x)
{
int y=fa[x],d=(son[y][1]==x);
if(fa[y]&&!isroot(y))
son[fa[y]][son[fa[y]][1]==y]=x;
fa[x]=fa[y];
fa[y]=x;
son[y][d]=son[x][d^1];
if(son[x][d^1])
fa[son[x][d^1]]=y;
son[x][d^1]=y;
update(y);update(x);
}
void splay(int x)
{
top=0;
st[++top]=x;
int tmp=x;
while(!isroot(tmp))
{
st[++top]=fa[tmp];
tmp=fa[tmp];
}
while(top)
push_down(st[top--]);
while(!isroot(x))
rotate(x);
}
void aksess(int x)
{
int t=0;
while(x)
{
splay(x);
son[x][1]=t;
update(x);
t=x;x=fa[x];
}
}
void beroot(int x)
{
aksess(x);
splay(x);
update_rev(x);
}
void link(int x,int y)
{
beroot(x);
fa[x]=y;
splay(x);
}
void split(int x,int y)
{
beroot(x);
aksess(y);
splay(y);
}
}LCT;
int main()
{
#ifndef ONLINE_JUDGE
freopen("tour.in","r",stdin);
freopen("tour.out","w",stdout);
#endif
scanf("%d",&n);
int id=n;
for(int i=1;i//DEBUG("%d\n",i);
int a,b,c;
scanf("%d %d %d",&a,&b,&c);
a++,b++;
LCT.v[++id]=c,LCT.update(id);//插节点注意update
f[i]=id;
LCT.link(a,id),LCT.link(b,id);
}
scanf("%d",&m);
for(int i=1;i<=m;i++)
{
scanf("%s",s);
int x,y;
Read(x),Read(y);
if(s[0]=='C')
{
LCT.splay(f[x]);
LCT.v[f[x]]=y;
LCT.update(f[x]);
}
else if(s[0]=='N')
{
LCT.split(x+1,y+1);
LCT.update_neg(y+1);
}
else if(s[0]=='S')
{
LCT.split(x+1,y+1);
printf("%d\n",LCT.sum[y+1]);
}
else if(s[1]=='A')
{
LCT.split(x+1,y+1);
printf("%d\n",LCT.mx[y+1]);
}
else
{
LCT.split(x+1,y+1);
printf("%d\n",LCT.mn[y+1]);
}
}
}