BJOI2014 大融合

BJOI2014 大融合

LCT维护子树

这题要维护的是子树的大小,动态加边,用LCT维护

\(sz_i\) 表示虚儿子的大小之和,\(sum_i\) 表示子树大小

考虑\(sz\)什么时候会变

  1. access中,只有一个儿子由虚变实,有一个儿子由实变虚,加减一下就好了

  2. 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\n",1ll*sum[x]*(sum[y]-sum[x]));
		}
	}
	return 0;
}
posted @ 2020-03-08 16:46  lcyfrog  阅读(140)  评论(0编辑  收藏  举报