BJOI2014大融合

Description

小强要在N个孤立的星球上建立起一套通信系统。这套通信系统就是连接N个点的一个树。这个树的边是一条一条添加上去的。在某个时刻,一条边的负载就是它所在的当前能够联通的树上路过它的简单路径的数量。 
现在,你的任务就是随着边的添加,动态的回答小强对于某些边的负载的询问。

Input

第一行包含两个整数N,Q,表示星球的数量和操作的数量。星球从1开始编号。
接下来的Q行,每行是如下两种格式之一:
A x y 表示在x和y之间连一条边。保证之前x和y是不联通的。
Q x y 表示询问(x,y)这条边上的负载。保证x和y之间有一条边。

Output

对每个查询操作,输出被查询的边的负载。

Sample Input

8 6

A 2 3

A 3 4

A 3 8

A 8 7

A 6 5

Q 3 8

Sample Output

6

Hint

对于40%的数据,N,Q≤1000
对于100%的数据,1≤N,Q≤100000

发现,负载=size[x]的子树*size[y]的子树

那么,LCT维护子树信息的裸题(入门),除了维护本身的实儿子,还有虚儿子信息需要维护,怎么做?link和access时,修改的右儿子加入“实”信息维护,删除的右儿子加入“虚”信息维护。

#include<bits/stdc++.h>
using namespace std;
#define Inc(i,L,r) for(register int i=(L);i<=(r);++i)
const int N = 1e5+10;
struct Splay{
	bool rev[N];
	int p[N],ch[N][2];
	int siz[N],ft[N];//总信息,虚子树信息(不包括自己) 
	#define Ls(v) ch[v][0]
	#define rs(v) ch[v][1]
	inline bool isroot(int x){
		return (Ls(p[x])^x)&&(rs(p[x])^x);
	}
	inline void pushdown(int x){
		if(rev[x]){
			rev[Ls(x)]^=1,rev[rs(x)]^=1;
			swap(Ls(x),rs(x));
			rev[x]=0;
		}
	}
	inline void maintain(int x){
		siz[x]=siz[Ls(x)]+siz[rs(x)]+ft[x]+1;
	}
	inline void rot(int x){
		int f=p[x],gf=p[f],type=rs(f)==x,son=ch[x][!type];
		if(!isroot(f))ch[gf][rs(gf)==f]=x;p[x]=gf;
		ch[p[son]=f][type]=son,maintain(f);
		ch[p[f]=x][!type]=f,maintain(x);
	}
	int top,stk[N];
	inline void splay(int x){
		stk[++top]=x;
		if(!isroot(x))for(int i=x;!isroot(i);i=p[i])stk[++top]=p[i];
		while(top)pushdown(stk[top--]);
		while(!isroot(x)){
			if(isroot(p[x]))return rot(x),void();
			if((rs(p[p[x]])==p[x])==(rs(p[x])==x))rot(p[x]);
			rot(x);
		}
	}
};
struct LCT{
	Splay sp;
	inline void access(int x){
		for(int lastx=0;x;lastx=x,x=sp.p[x])
			sp.splay(x),sp.ft[x]+=sp.siz[sp.rs(x)],sp.rs(x)=lastx,sp.ft[x]-=sp.siz[sp.rs(x)],sp.maintain(x);//删除的加入虚树中 
	}
	inline void makeroot(int x){
		access(x),sp.splay(x),sp.rev[x]^=1;
	}
	inline void link(int x,int y){
		makeroot(x),makeroot(y);
		sp.p[x]=y,sp.ft[y]+=sp.siz[x],sp.maintain(y);
	}
	inline void split(int x,int y){
		makeroot(x),access(y),sp.splay(y);
	}
}lct;
int main(){
	int n,Q;scanf("%d%d",&n,&Q);
	while(Q--){
		char op=getchar();while(op!='A'&&op!='Q')op=getchar();
		int x,y;scanf("%d%d",&x,&y);
		switch(op){
			case 'A':lct.link(x,y);break;
			case 'Q':lct.split(x,y);cout<<(lct.sp.ft[x]+1)*(lct.sp.ft[y]+1)<<"\n";break;//取链之后,x没有孩子,y仅有x一个儿子且虚儿子为各自子树 
		}
	}
	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值