BZOJ4530 大融合
Description
小强要在N个孤立的星球上建立起一套通信系统。这套通信系统就是连接N个点的一个树。
这个树的边是一条一条添加上去的。在某个时刻,一条边的负载就是它所在的当前能够
联通的树上路过它的简单路径的数量。
例如,在上图中,现在一共有了5条边。其中,(3,8)这条边的负载是6,因
为有六条简单路径2-3-8,2-3-8-7,3-8,3-8-7,4-3-8,4-3-8-7路过了(3,8)。
现在,你的任务就是随着边的添加,动态的回答小强对于某些边的负载的
询问。
Input
第一行包含两个整数N,Q,表示星球的数量和操作的数量。星球从1开始编号。
接下来的Q行,每行是如下两种格式之一:
A x y 表示在x和y之间连一条边。保证之前x和y是不联通的。
Q x y 表示询问(x,y)这条边上的负载。保证x和y之间有一条边。
1≤N,Q≤100000
题目中要求的负载就是以一条边端点为根的两个子树的大小相乘。而动态加边的操作很容易想到LCT。一般的LCT通过伸展树维护的是一条链上的信息,但这并不意味着不能使用LCT。
开两个数组
Size[x]
和
sz[x]
,分别表示以
x
为根的子树的大小、
也就是把 Size 划分为了伸展树里节点的 Size 之和、虚儿子的 Size 之和。那么现在的问题在于如何维护 sz 数组。注意到本题用到的 Access 操作中,每次向上跳都最多只会将一条实边改为虚边、一条虚边改为实边; Link 操作直接是给一个点增加一个虚儿子。所以在这两个操作中维护一下 sz 即可。
#include<stdio.h>
#include<algorithm>
#define MAXN 200005
#define ll long long
using namespace std;
int N,Q;
int ls[MAXN],rs[MAXN],fa[MAXN],rev[MAXN],Size[MAXN],sz[MAXN];
bool isrt(int x){return ls[fa[x]]!=x&&rs[fa[x]]!=x;}
void Update(int p)
{
Size[p]=Size[ls[p]]+Size[rs[p]]+sz[p]+1;
//Size[p]=Size[rs[p]]+sz[p]+1;
}
void Putdown(int p)
{
if(rev[p]==0)return;
rev[p]=0;rev[ls[p]]^=1;rev[rs[p]]^=1;
swap(ls[p],rs[p]);
}
void Zig(int x)
{
int y=fa[x],z=fa[y];
if(!isrt(y))
{
if(ls[z]==y)ls[z]=x;
else rs[z]=x;
}
fa[x]=z;fa[y]=x;fa[rs[x]]=y;
ls[y]=rs[x];rs[x]=y;
Update(y);Update(x);
}
void Zag(int x)
{
int y=fa[x],z=fa[y];
if(!isrt(y))
{
if(ls[z]==y)ls[z]=x;
else rs[z]=x;
}
fa[x]=z;fa[y]=x;fa[ls[x]]=y;
rs[y]=ls[x];ls[x]=y;
Update(y);Update(x);
}
int s[MAXN],Top;
void Splay(int x)
{
int i,y,z;
s[++Top]=x;
for(i=x;!isrt(i);i=fa[i])s[++Top]=fa[i];
while(Top)Putdown(s[Top--]);
while(!isrt(x))
{
y=fa[x];z=fa[y];
if(isrt(y))
{
if(ls[y]==x)Zig(x);
else Zag(x);
}
else
{
if(ls[z]==y)
{
if(ls[y]==x)Zig(y),Zig(x);
else Zag(x),Zig(x);
}
else
{
if(rs[y]==x)Zag(y),Zag(x);
else Zig(x),Zag(x);
}
}
}
}
void Access(int x)
{
int t=0;
while(x)
{
Splay(x);
sz[x]=sz[x]-Size[t]+Size[rs[x]];
rs[x]=t;
Update(x);
t=x;x=fa[x];
}
}
void SetRt(int x)
{
Access(x);Splay(x);rev[x]^=1;
}
void Link(int x,int y)
{
SetRt(x);SetRt(y);
sz[y]+=Size[x];
Update(y);
Splay(y);
fa[x]=y;
}
int main()
{
int i,x,y;
char op[3];
ll Ans;
scanf("%d%d",&N,&Q);
for(i=1;i<=N;i++)Size[i]=1;
while(Q--)
{
scanf("%s%d%d",op,&x,&y);
if(op[0]=='A')Link(x,y);
else
{
SetRt(x);Access(y);
Ans=sz[y]+1;
SetRt(y);Access(x);
Ans*=(sz[x]+1);
printf("%lld\n",Ans);
}
}
}