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;
}