题目链接
QWQ
这个题目是LCT维护子树信息的经典应用
根据题目信息来看,对于一个这条边的两个端点各自的 s i z e size size乘起来,不过这个应该算呢?
我们可以考虑在LCT上多维护一个 x v [ i ] xv[i] xv[i]表示 i i i的虚子树的子树和,然后维护 s u m [ i ] sum[i] sum[i]表示 i i i的虚+实子树之和。
那么对于一个点 x x x,他在原树上的字数大小就应该是 s i z e = x v [ x ] + s u m [ c h [ x ] [ 1 ] ] + 1 size = xv[x]+sum[ch[x][1]]+1 size=xv[x]+sum[ch[x][1]]+1
这是个经典套路!
对于这个题来说,我们可以通过 s p l i t ( x , y ) split(x,y) split(x,y),然后 a n s ans ans就等于 ( x v [ x ] + 1 ) × ( x v [ y ] + 1 ) (xv[x]+1)\times (xv[y]+1) (xv[x]+1)×(xv[y]+1)
这个地方可以理解为,x的虚儿子是以x为根,不经过 ( x , y ) (x,y) (x,y)这条边的 所有子树和,正好符合题目要求,y也是同理。
当然,也存在别的计算方法:我们 m a k e r o o t ( y ) makeroot(y) makeroot(y),然后直接用x的子树大小,乘上y的子树大小减去x的。也是可以的。道理和上面的类似
直接上代码
// luogu-judger-enable-o2
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<vector>
#include<queue>
#include<cmath>
#include<map>
#include<set>
using namespace std;
inline int read()
{
int x=0,f=1;char ch=getchar();
while (!isdigit(ch)) {if (ch=='-') f=-1;ch=getchar();}
while (isdigit(ch)) {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
return x*f;
}
const int maxn = 2e5+1e2;
int size[maxn];
int xv[maxn];
int ch[maxn][3];
int fa[maxn];
int n,m,cnt;
int st[maxn];
int rev[maxn];
int son(int x)
{
if (ch[fa[x]][0]==x) return 0;
else return 1;
}
bool notroot(int x)
{
return ch[fa[x]][0]==x || ch[fa[x]][1]==x;
}
void update(int x)
{
size[x]=size[ch[x][0]]+size[ch[x][1]]+xv[x]+1;
}
void reverse(int x)
{
swap(ch[x][0],ch[x][1]);
rev[x]^=1;
}
void pushdown(int x)
{
if (rev[x])
{
if (ch[x][0]) reverse(ch[x][0]);
if (ch[x][1]) reverse(ch[x][1]);
rev[x]=0;
}
}
void rotate(int x)
{
int y=fa[x],z=fa[y];
int b=son(x),c=son(y);
if (notroot(y)) ch[z][c]=x;
fa[x]=z;
ch[y][b]=ch[x][!b];
fa[ch[x][!b]]=y;
ch[x][!b]=y;
fa[y]=x;
update(y);
update(x);
}
void splay(int x)
{
int y=x,cnt=0;
st[++cnt]=y;
while (notroot(y)) y=fa[y],st[++cnt]=y;
while (cnt) pushdown(st[cnt--]);
while (notroot(x))
{
int y=fa[x],z=fa[y];
int b=son(x),c=son(y);
if (notroot(y))
{
if (b==c) rotate(y);
else rotate(x);
}
rotate(x);
}
update(x);
}
void access(int x)
{
for (int y=0;x;y=x,x=fa[x])
{
splay(x);
xv[x]+=size[ch[x][1]];
ch[x][1]=y;
xv[x]-=size[y];
}
}
void makeroot(int x)
{
access(x);
splay(x);
reverse(x);
}
int findroot(int x)
{
access(x);
splay(x);
while (ch[x][0])
{
pushdown(x);
x=ch[x][0];
}
return x;
}
void split(int x,int y)
{
makeroot(x);
access(y);
splay(y);
}
void link(int x,int y)
{
split(x,y);
if (findroot(y)!=x)
{
fa[x]=y;
xv[y]+=size[x];
}
update(y);
}
int q;
int main()
{
n=read(),q=read();
for (int i=1;i<=q;i++)
{
char s[10];
scanf("%s",s+1);
if (s[1]=='A')
{
int x=read(),y=read();
link(x,y);
}
else
{
int x=read(),y=read();
split(x,y);
printf("%lld\n",1ll*(xv[x]+1)*(xv[y]+1));
}
}
return 0;
}