[BZOJ4530][BJOI2014]大融合(LCT维护子树信息)

11 篇文章 0 订阅

题目:

我是超链接

题解:

上次我是运用的树链剖分+并查集做的,但事实上看到连接边应该想到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));
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值