bzoj4530 [Bjoi2014]大融合 (LCT维护子树信息)

83 篇文章 0 订阅

bzoj4530 [Bjoi2014]大融合

原题地址http://www.lydsy.com/JudgeOnline/problem.php?id=4530

题意:
小强要在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)。
现在,你的任务就是随着边的添加,动态的回答小强对于某些边的负载的
询问。

第一行包含两个整数N,Q,表示星球的数量和操作的数量。星球从1开始编号。
接下来的Q行,每行是如下两种格式之一:
A x y 表示在x和y之间连一条边。保证之前x和y是不联通的。
Q x y 表示询问(x,y)这条边上的负载。保证x和y之间有一条边。

数据范围
1≤N,Q≤100000

题解:
neither_nor学长的讲解

首先为了维护子树信息,我们需要维护每个点实子树和虚子树的信息。(实子树的信息包括实儿子的虚子树的信息)
为什么?
尽管一个点x的实子树中并不全是他原树的子树(例如他实际的fa),
但如果 access(x),则 x的虚子树就是原树中的x的子树。
因此,只需要维护子树信息(实/虚),access后查询即可。
改变虚子树信息的只是 access和link
这时的,update就是 自己信息+ls信息+rs信息+虚子树信息。

对于这道题:
就是 size = ls.size+rs.size+虚子树size+1
改变虚子树时要减掉改变的,加上连上的子树信息。
ans=size[x]*(size总-size[x])

注意:
link x到y上前,要先把其中一方makeroot,
这和bzoj2002不同,x和y都可能有原树的父亲,x可能有ls,即真实的fa,现在又来个fa显然不行。

另外link后也要update,因为虚子树改变了。

代码:

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=100005;
int n,q;
struct node
{
    int ch[2],fa,size,ss;
    bool rev;
    void init() {fa=ch[1]=ch[0]=ss=0;rev=0;size=1;}
}tr[N];
struct  Link_Cut_Tree
{
    bool isroot(int x) {return (tr[tr[x].fa].ch[0]!=x&&tr[tr[x].fa].ch[1]!=x);}
    void pushdown(int x)
    {
        if(tr[x].rev) 
        {
            swap(tr[x].ch[0],tr[x].ch[1]);
            if(tr[x].ch[0]) tr[tr[x].ch[0]].rev^=1;
            if(tr[x].ch[1]) tr[tr[x].ch[1]].rev^=1;
            tr[x].rev^=1;
        }
    }
    void push(int x)
    {
        if(!isroot(x)) push(tr[x].fa);
        pushdown(x);
    }
    void update(int x)
    {
        int ls=tr[x].ch[0]; int rs=tr[x].ch[1];
        tr[x].size=tr[x].ss+1+tr[ls].size+tr[rs].size;
    }
    void rotate(int x)
    {
        int y=tr[x].fa; int z=tr[y].fa;
        int l=(tr[y].ch[0]==x)?0:1; int r=l^1;
        if(!isroot(y)) {if(tr[z].ch[0]==y) tr[z].ch[0]=x; else tr[z].ch[1]=x;}
        tr[x].fa=z;
        tr[y].ch[l]=tr[x].ch[r]; tr[tr[x].ch[r]].fa=y;
        tr[x].ch[r]=y; tr[y].fa=x;
        update(y); update(x);
    }
    void splay(int x)
    {
        push(x);
        while(!isroot(x))
        {
            int y=tr[x].fa; int z=tr[y].fa;
            if(!isroot(y))
            {
                if((tr[y].ch[0]==x)^(tr[z].ch[0]==y)) rotate(x);
                else rotate(y);
            }
            rotate(x);
        }
    }
    void access(int x)
    {
        int y=0;
        for(;x;y=x,x=tr[x].fa)
        {
            splay(x);
            tr[x].ss+=tr[tr[x].ch[1]].size;
            tr[x].ch[1]=y; tr[x].ss-=tr[y].size; 
            update(x);
        }
    }
    void makeroot(int x)
    {
        access(x); splay(x); tr[x].rev=1;
    }
    void link(int x,int y)
    {
        makeroot(x);    access(y); splay(y); //必须要makeroot[x] 因为若x不是根就有可能有原树上的fa,即ls,此时又加了一个fa,就有两个fa了。 
        tr[x].fa=y; tr[y].ss+=tr[x].size; 
        update(y);//!!!!!! 这里size也包含虚树的,所以ss改变要update 
    }
}LCT;
int main()
{
    scanf("%d%d",&n,&q);
    for(int i=0;i<=n;i++) tr[i].init(); tr[0].size=0;
    while(q--)
    {
        char opt[5];int x,y; scanf("%s",opt); scanf("%d%d",&x,&y);
        if(opt[0]=='A') LCT.link(x,y);
        else
        { 
            LCT.makeroot(x);
            LCT.access(y);
            LCT.splay(x);
            printf("%lld\n",1LL*(tr[y].ss+1)*(tr[x].size-tr[y].ss-1));
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值