BJOI2014 大融合
LCT维护子树
这题要维护的是子树的大小,动态加边,用LCT维护
设(sz_i) 表示虚儿子的大小之和,(sum_i) 表示子树大小
考虑(sz)什么时候会变
-
access中,只有一个儿子由虚变实,有一个儿子由实变虚,加减一下就好了
-
link之后连了虚儿子,需要更新,注意这个时候两个点都必须是根,不然要往上pushup
另外pushup也要改一下
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<bitset>
#include<set>
#define ls ch[x][0]
#define rs ch[x][1]
using namespace std;
inline int read(){
int x=0,pos=1;char ch=getchar();
for(;!isdigit(ch);ch=getchar()) if(ch=='-') pos=0;
for(;isdigit(ch);ch=getchar()) x=(x<<1)+(x<<3)+ch-'0';
return pos?x:-x;
}
const int N = 100201;
int ch[N][2],fa[N],sum[N],r[N],sz[N];
char s[2];
int nroot(int x){return ch[fa[x]][1]==x||ch[fa[x]][0]==x;}
void push_up(int x){sum[x]=sum[ls]+sum[rs]+sz[x]+1;}
void pushr(int x){swap(ls,rs);r[x]^=1;}
void push_down(int x){if(r[x]){if(ls)pushr(ls); if(rs)pushr(rs); r[x]=0; }}
void push(int x){if(nroot(x)) push(fa[x]);push_down(x);}
int get(int x){return ch[fa[x]][1]==x;}
void rotate(int x){
int f=fa[x],g=fa[f],kx=get(x),kf=get(f);
if(nroot(f)) ch[g][kf]=x;fa[x]=g;
ch[f][kx]=ch[x][kx^1];fa[ch[x][kx^1]]=f;
ch[x][kx^1]=f;fa[f]=x;
push_up(f);push_up(x);
}
void splay(int x){
push(x);
while(nroot(x)){
int f=fa[x];
if(nroot(f)) rotate(get(x)==get(f)?f:x);
rotate(x);
}
}
void access(int x){
for(int y=0;x;y=x,x=fa[x]){
splay(x);sz[x]+=sum[rs]-sum[y];rs=y;push_up(x);
}
}
void makeroot(int x){access(x);splay(x);pushr(x);}
int findroot(int x){
access(x);splay(x);while(ch[x][0]) x=ch[x][0];splay(x);return x;
}
void link(int x,int y){
makeroot(x);
if(findroot(y)!=x){
makeroot(y);
fa[x]=y;sz[y]+=sum[x];push_up(y);
}
}
void split(int x,int y){
makeroot(x);access(y);splay(y);
}
int main(){
int n=read(),q=read();
for(int i=1;i<=n;i++) sum[i]=1;
for(int i=1;i<=q;i++){
scanf("%s",s);
if(s[0]=='A'){
int x=read(),y=read();link(x,y);
}else{
int x=read(),y=read();
split(x,y);
printf("%lld
",1ll*sum[x]*(sum[y]-sum[x]));
}
}
return 0;
}