BZOJ4530: [Bjoi2014]大融合【LCT求子树大小】

题意:动态加边,询问某条边两边连通块大小。

在线LCT做法:多记录一个 g [ x ] g[x] g[x]表示虚儿子的大小之和,access的时候一加一减维护一下。
离线线段树合并做法:先建出最终的森林,求出dfs序,求某一部分的大小就在连通块对应的线段树中查询dfs序上的一段区间。
离线链加做法:先建出最终的森林,加的边一定是父子边,并查集维护每个点向上能连通的最远的点,加边相当于将儿子的size加到父亲到最远点的这条链上。

没有标记的LCT维护子树可加信息很easy。

Code(LCT):

#include<bits/stdc++.h>
#define maxn 100005
using namespace std;
namespace LCT{
	int ch[maxn][2],fa[maxn],siz[maxn],g[maxn];
	bool rev[maxn];
	#define il inline
	#define pa fa[x]
	il bool isc(int x){return ch[pa][1]==x;}
	il bool isr(int x){return ch[pa][0]!=x&&ch[pa][1]!=x;}
	il void pd(int x){
		if(rev[x]){
			swap(ch[x][0],ch[x][1]),rev[x]=0;
			rev[ch[x][0]]^=1,rev[ch[x][1]]^=1;
		}
	}
	il void pdpath(int x){if(!isr(x)) pdpath(pa); pd(x);}
	il void upd(int x){siz[x]=siz[ch[x][0]]+siz[ch[x][1]]+g[x]+1;}
	il void rot(int x){
		int y=fa[x],z=fa[y],c=isc(x);
		if(!isr(y)) ch[z][isc(y)]=x;
		fa[ch[y][c]=ch[x][!c]]=y,fa[ch[x][!c]=y]=x,fa[x]=z;
		upd(y),upd(x);
	}
	il void splay(int x){
		for(pdpath(x);!isr(x);rot(x))
			if(!isr(pa)) rot(isc(pa)==isc(x)?pa:x);
	}
	il void access(int x){
		for(int y=0;x;x=fa[y=x]) 
			splay(x),g[x]+=siz[ch[x][1]]-siz[y],ch[x][1]=y,upd(x); 
	}
	il void bert(int x){access(x),splay(x),rev[x]^=1;}
	il void link(int x,int y){
		bert(x),access(y),splay(y),fa[x]=y;
		g[y]+=siz[x],siz[y]+=siz[x];
	}
}
using namespace LCT;
int n,m;
int main()
{
	int x,y; char op[2];
	scanf("%d%d",&n,&m);
	while(m--){
		scanf("%s%d%d",op,&x,&y);
		if(op[0]=='A') link(x,y);
		else bert(x),access(y),printf("%lld\n",1ll*(g[x]+1)*(g[y]+1));
		//bert(x),access(y),splay(y),printf("%lld\n",1ll*siz[x]*(siz[y]-siz[x]));
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值