[BJOI2014]大融合

链接:https://www.luogu.org/problemnew/show/P4219

显然答案左边子树大小乘右边,LCT可以维护,sz记录的是所有子树sz和(不仅仅是左右孩子),分类讨论一下在rotate里更新就行了,其它地方也不用push_up,需要注意的是连边是两个点都要转到根,因为这样才可以不用考虑有祖先要向上传递的情况(之前没想到WA0 × 3).

代码:

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=1e5+100;

int sz[N],ch[N][2],fa[N];
bool rev[N];

int get(int x)
{
    if(!fa[x])return -1;
    if(ch[fa[x]][0]==x)return 0;
    if(ch[fa[x]][1]==x)return 1;
    return -1;
}

void R(int x)
{
    swap(ch[x][0],ch[x][1]);
    rev[x]^=1;
}

void push_down(int x)
{
    if(x&&rev[x])
    {
        if(ch[x][0])R(ch[x][0]);
        if(ch[x][1])R(ch[x][1]);
        rev[x]=0;
    }
}

void push_all(int x)
{
    if(get(x)!=-1)push_all(fa[x]);
    push_down(x);
}

void rot(int x)
{
    int fx=fa[x],gx=fa[fx],op=get(x),fop=get(fx),s1=sz[ch[x][op^1]],s2=sz[x],s3=sz[fx];
    fa[x]=gx;if(fop!=-1)ch[gx][fop]=x;
    ch[fx][op]=ch[x][op^1],fa[ch[x][op^1]]=fx;
    fa[fx]=x,ch[x][op^1]=fx;
    sz[x]=s3,sz[fx]=s3-s2+s1;
}

void splay(int x)
{
    push_all(x);
    for(int fx=fa[x];get(x)!=-1;rot(x),fx=fa[x])
        if(get(fx)!=-1)get(fx)==get(x)?rot(fx):rot(x);
}

void access(int x)
{
    for(int y=0;x;y=x,x=fa[x])
        splay(x),ch[x][1]=y;
}

void make_rt(int x)
{access(x),splay(x),R(x);}

void link(int x,int y)
{make_rt(x),make_rt(y),fa[x]=y,sz[y]+=sz[x];}

void ask(int x,int y)
{
    make_rt(x),access(y),splay(y);
    ll res;
    res=1LL*sz[x]*(sz[y]-sz[x]);
    printf("%lld\n",res);
}

int n,m;

int main()
{
    char op[3];int x,y;
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)sz[i]=1;
    for(int i=1;i<=m;i++)
    {
        scanf("%s",op);
        scanf("%d%d",&x,&y);
        if(op[0]=='A')link(x,y);
        else ask(x,y);
    }
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值