题意:动态加边,询问某条边两边连通块大小。
在线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]));
}
}