给出 n n n个独立的点,处理以下 q q q个操作,每个操作只会是如下操作之一
(1)连接 x , y x,y x,y,保证原本没有边存在
(2)查询边 ( x , y ) (x,y) (x,y)的负荷,定义负荷为树上经过 ( x , y ) (x,y) (x,y)的简单路径的个数
设 s i z e size size是当前结点为根的情况下的子树信息和, v s i z e ( v i r t u a l _ s i z e ) vsize(virtual\_size) vsize(virtual_size)是当前结点为根的情况下的所有虚儿子的子树和。
如果给定根,即已知 x x x的父节点 f a fa fa,那么只需要 s p l i t ( x , f a ) split(x,fa) split(x,fa), x x x的 v s i z e vsize vsize就已经是原树的子树信息和了。
更一般的, s i z e size size维护的是原树这个图的连接的所有连通分量的信息和,如果给定根要找出子树信息和,只需要剥离他的父亲即可。
另外,在维护子树信息时, l i n k ( x , y ) link(x,y) link(x,y)时,最好先 s p l a y ( y ) splay(y) splay(y)。因为 y y y也有父亲,给 y y y连接了虚儿子后,也需要 p u s h _ u p y push\_up\ y push_up y的父亲,先 s p l a y ( y ) splay(y) splay(y)后, y y y就没有父亲了,也就不需要更新。
也可以在连接虚儿子后 s p l a y ( y ) splay(y) splay(y),此时的含义就是手动更新 y y y的祖先们。
维护子树信息只需要在更改了虚实关系的函数更新 v s i z e vsize vsize即可,仅需在 l i n k , a c c e s s link,access link,access中更新
#include
#define ll long long
using namespace std;
struct LCT
{
struct node
{
int v,tag,son[2],fa,size,vsize;
};
stack<int>s;
vector<node>tree;
LCT():tree(100005){}
bool isroot(int x)
{
return tree[tree[x].fa].son[0]!=x&&tree[tree[x].fa].son[1]!=x;
}
int getson(int x,int y)
{
return x==tree[y].son[1];
}
void push_up(int x)
{
tree[x].size=1+tree[x].vsize+tree[tree[x].son[0]].size+tree[tree[x].son[1]].size;
}
void rotate(int x)
{
int y=tree[x].fa,z=tree[y].fa;
int k1=getson(x,y),k2=getson(y,z);
tree[x].fa=z;
if(!isroot(y)) tree[z].son[k2]=x;
tree[y].son[k1]=tree[x].son[!k1];
if(tree[x].son[!k1]) tree[tree[x].son[!k1]].fa=y;
tree[x].son[!k1]=y;
tree[y].fa=x;
push_up(y); push_up(x);
}
void reverse(int x)
{
swap(tree[x].son[0],tree[x].son[1]);
tree[x].tag^=1;
}
void push_down(int x)
{
if(!tree[x].tag) return;
for(int i=0;i<=1;i++)
if(tree[x].son[i]) reverse(tree[x].son[i]);
tree[x].tag=0;
}
void splay(int x)
{
int now=x;
push_up(now);
s.push(now);
while(!isroot(now))
{
s.push(tree[now].fa);
// push_up(tree[now].fa);
now=tree[now].fa;
}
while(!s.empty())
{
push_down(s.top());
s.pop();
}
while(!isroot(x))
{
int y=tree[x].fa,z=tree[y].fa;
int k1=getson(x,y),k2=getson(y,z);
if(!isroot(y))
{
if(k1==k2) rotate(y);
else rotate(x);
}
rotate(x);
}
}
void access(int x)//打通root->x的splay
{
int last=0;
while(x)
{
splay(x);
tree[x].vsize+=tree[tree[x].son[1]].size-tree[last].size;
tree[x].son[1]=last;
push_up(last=x);
x=tree[x].fa;
}
}
void makeroot(int x)
{
access(x);
splay(x);
reverse(x);
}
int findroot(int x)
{
access(x);
splay(x);
while(tree[x].son[0])
{
push_down(x);
x=tree[x].son[0];
}
splay(x);
return x;
}
void split(int x,int y)
{
makeroot(x);
access(y);
splay(y);
}
void link(int x,int y)
{
makeroot(x);
if(x==findroot(y)) return;
splay(y);
tree[x].fa=y;
tree[y].vsize+=tree[x].size;
push_up(y);
}
// void cut(int x,int y)
// {
// makeroot(x);
// if(findroot(y)!=x||tree[y].fa!=x||tree[y].son[0]) return;
// tree[y].fa=tree[x].son[1]=0;
// }
}lct;
int n,q;
int main()
{
scanf("%d%d",&n,&q);
while(q--)
{
char s[5]; int x,y;
scanf("%s%d%d",s+1,&x,&y);
if(s[1]=='A') lct.link(x,y);
else
{
lct.split(x,y);
printf("%lld\n",1ll*(lct.tree[x].vsize+1)*(lct.tree[y].vsize+1));
}
}
return 0;
}