题意
给定一个 (n) 个点的无边无向图,有 (q) 次操作,每次操作分为以下两种:
A x y
:将 (x) 和 (y) 连边,保证 (x) 和 (y) 不连通。
Q x y
:询问图中有多少条路径经过 ((x,y))。
( exttt{Data Range:}1leq n,qleq 10^5)
题解
神仙题。
注意到询问的答案其实是以 (x) 为根的子树大小与以 (y) 为根的子树大小的乘积。
考虑使用 ( exttt{LCT}) 维护虚子树大小,这个与普通的 ( exttt{LCT}) 在写法上有一些差别。
第一个是在 access
的时候:(其中 (sz) 是子树大小,(si) 是虚子树大小)
inline void access(ll x)
{
for(register int i=0;x;x=nd[i=x].fa)
{
splay(x),nd[x].si+=nd[rs].sz,nd[x].si-=nd[rs=i].sz,update(x);
}
}
在 access
的时候,原来连接自己与右儿子的边由实的变成了虚的,而新的边由虚的变成了实的,所以这里要统计一下贡献。
第二个是在 link
的时候:
inline void link(ll x,ll y)
{
split(x,y),nd[nd[x].fa=y].si+=nd[x].sz,update(y);
}
首先把 (x) 和 (y) 的链抠出来全部变成实链,因为一个点连到儿子的的只能有一条是实链,所以 ((x,y)) 是虚链,要统计一次贡献。
然后答案就是 ((si_x+1)(si_y+1)),就成了。
代码
#include<bits/stdc++.h>
using namespace std;
typedef int ll;
typedef long long int li;
const ll MAXN=2e5+51;
ll n,qcnt,x,y;
char ch;
inline ll read()
{
register ll num=0,neg=1;
register char ch=getchar();
while(!isdigit(ch)&&ch!='-')
{
ch=getchar();
}
if(ch=='-')
{
neg=-1;
ch=getchar();
}
while(isdigit(ch))
{
num=(num<<3)+(num<<1)+(ch-'0');
ch=getchar();
}
return num*neg;
}
namespace LCT{
struct Node{
ll fa,rv,sz,si;
ll ch[2];
};
struct LinkCutTree{
Node nd[MAXN];
ll st[MAXN];
#define ls nd[x].ch[0]
#define rs nd[x].ch[1]
inline bool nroot(ll x)
{
return nd[nd[x].fa].ch[0]==x||nd[nd[x].fa].ch[1]==x;
}
inline void update(ll x)
{
nd[x].sz=nd[ls].sz+nd[rs].sz+nd[x].si+1;
}
inline void reverse(ll x)
{
swap(ls,rs),nd[x].rv^=1;
}
inline void spread(ll x)
{
if(nd[x].rv)
{
ls?reverse(ls):(void)(1),rs?reverse(rs):(void)(1);
nd[x].rv=0;
}
}
inline void rotate(ll x)
{
ll fa=nd[x].fa,gfa=nd[fa].fa;
ll dir=nd[fa].ch[1]==x,son=nd[x].ch[!dir];
if(nroot(fa))
{
nd[gfa].ch[nd[gfa].ch[1]==fa]=x;
}
nd[x].ch[!dir]=fa,nd[fa].ch[dir]=son;
if(son)
{
nd[son].fa=fa;
}
nd[fa].fa=x,nd[x].fa=gfa,update(fa);
}
inline void splay(ll x)
{
ll fa=x,gfa,cur=0;
st[++cur]=fa;
while(nroot(fa))
{
st[++cur]=fa=nd[fa].fa;
}
while(cur)
{
spread(st[cur--]);
}
while(nroot(x))
{
fa=nd[x].fa,gfa=nd[fa].fa;
if(nroot(fa))
{
rotate((nd[fa].ch[0]==x)^(nd[gfa].ch[0]==fa)?x:fa);
}
rotate(x);
}
update(x);
}
inline void access(ll x)
{
for(register int i=0;x;x=nd[i=x].fa)
{
splay(x),nd[x].si+=nd[rs].sz,nd[x].si-=nd[rs=i].sz,update(x);
}
}
inline void makeRoot(ll x)
{
access(x),splay(x),reverse(x);
}
inline void split(ll x,ll y)
{
makeRoot(x),access(y),splay(y);
}
inline void link(ll x,ll y)
{
split(x,y),nd[nd[x].fa=y].si+=nd[x].sz,update(y);
}
#undef ls
#undef rs
};
}
LCT::LinkCutTree lct;
int main()
{
n=read(),qcnt=read();
for(register int i=1;i<=n;i++)
{
lct.nd[i].sz=1;
}
for(register int i=0;i<qcnt;i++)
{
cin>>ch,x=read(),y=read();
if(ch=='A')
{
lct.link(x,y);
}
if(ch=='Q')
{
lct.split(x,y);
printf("%lld
",(li)(lct.nd[x].si+1)*(lct.nd[y].si+1));
}
}
}