题目:
题解:
上次我是运用的树链剖分+并查集做的,但事实上看到连接边应该想到LCT,这个负载很明显是两个子树的节点数相乘,那么我们只要在LCT的同时维护一波信息就好了
维护什么信息?我们要维护的是虚子树的节点数量!有一个操作叫做split,意思是把除x,y这条边之外和x,y相连的splay边断掉。实现起来很简单,reverse+access+splay就好了。这样做完之后,我们把两个节点虚子树的数量+1(自己)相乘就是答案了
然后link操作如果不把y access+splay到根节点就会出现问题,因为不这样做,y的所有祖先的size值将得不到更新,只要不access y,y的祖先虚子树的值就不会改变,所以干脆先access+splay y
不知道比上一个方法简单多少
代码:
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
const int N=100005;
int size[N],ch[N][2],deltaz[N],f[N],stack[N],sizex[N];
void updata(int now){size[now]=size[ch[now][0]]+size[ch[now][1]]+sizex[now]+1;}
void pushdown(int now)
{
if (deltaz[now])
{
swap(ch[now][0],ch[now][1]);
deltaz[ch[now][0]]^=1;
deltaz[ch[now][1]]^=1;
deltaz[now]=0;
}
}
int get(int x){return ch[f[x]][1]==x;}
bool Is_root(int x){return ch[f[x]][0]!=x && ch[f[x]][1]!=x;}
void rotate(int x)
{
int old=f[x],oldf=f[old],which=get(x);bool gen=Is_root(old);
ch[old][which]=ch[x][which^1]; f[ch[x][which^1]]=old;
ch[x][which^1]=old; f[old]=x;
f[x]=oldf; if (!gen) ch[oldf][ch[oldf][1]==old]=x;
updata(old);
updata(x);
}
void splay(int x)
{
int top=0,i;
for (i=x;!Is_root(i);i=f[i]) stack[++top]=i;
stack[++top]=i;
for (i=top;i>=1;i--) pushdown(stack[i]);
for (;!Is_root(x);rotate(x))
if (!Is_root(f[x])) rotate(get(f[x])==get(x)?f[x]:x);
}
void access(int x)
{
int t=0;
for (;x;t=x,x=f[x])
{
splay(x);
sizex[x]-=size[t];
sizex[x]+=size[ch[x][1]];
ch[x][1]=t;
}
}
void reverse(int x){access(x);splay(x);deltaz[x]^=1;}
void split(int x,int y)
{
reverse(x);
access(y);splay(y);
}
void link(int x,int y){split(x);f[x]=y;sizex[y]+=size[x];updata(y);}
int main()
{
int n,Q;scanf("%d%d",&n,&Q);
for (int i=1;i<=n;i++) size[i]=1;
while (Q--)
{
char st[5];int x,y;
scanf("%s",st);
scanf("%d%d",&x,&y);
if (st[0]=='A') link(x,y);
else split(x,y),printf("%d\n",(sizex[x]+1)*(sizex[y]+1));
}
}