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

    解析:

    看到加边的操作,就能确定是用LCT维护了。但是splay在旋转的过程中,与之相连的儿子有时会被丢掉。这是我们可以考虑另开一个数组来维护那些在LCT上与之相连但splay上分开的儿子节点,然后就完了。要注意的是维护LCT上的节点的操作正好与Splay相反,这样才能保证一个节点的子数个数的不重不漏。

#include<iostream>
#include<cstdio>
#define M 100010
using namespace std;
int n,q;
int f[M],size[M],ch[M][2],rev[M],si[M];
void update(int x)
{
    size[x]=size[ch[x][0]]+size[ch[x][1]]+si[x]+1;
}
void pushdown(int x)
{   
    if(rev[x]&&x)
    {   
        if(ch[x][0]) rev[ch[x][0]]^=1;
        if(ch[x][1]) rev[ch[x][1]]^=1;
        swap(ch[x][0],ch[x][1]);
        rev[x]^=1;
    }
}
int is_root(int x)
{   
    return ch[f[x]][0]!=x&&ch[f[x]][1]!=x;
}
int get_son(int x)
{   
    return ch[f[x]][1]==x;
}
void rotate(int x)
{   
    int old=f[x],oldf=f[old],k=get_son(x);
    if(!is_root(old)) ch[oldf][ch[oldf][1]==old]=x;
    ch[old][k]=ch[x][k^1];
    f[ch[old][k]]=old;
    ch[x][k^1]=old;
    f[old]=x;
    f[x]=oldf;
    update(old);
    update(x);
}
void push(int x)
{   
    if(!is_root(x)) push(f[x]);
    pushdown(x);
}
void splay(int x)
{
    push(x);
    for(int fa;!is_root(x);rotate(x))
        if(!is_root(fa=f[x]))
            rotate(get_son(x)==get_son(fa)?fa:x);
}
void access(int x)
{
    for(int y=0;x;y=x,x=f[x])
    {
        splay(x);
        si[x]+=size[ch[x][1]];
        ch[x][1]=y;
        si[x]-=size[ch[x][1]];
    }
}
void makeroot(int x)
{
    access(x);
    splay(x);
    rev[x]^=1;
}
void link(int x,int y)
{
    makeroot(x);
    access(y);
    splay(y);
    f[x]=y;
    si[f[x]]+=size[x];
    update(y);
}
int query(int x,int y)
{
    makeroot(x);
    access(y);
    splay(y);
    return (size[y]-size[x])*size[x];
}
int main()
{
    scanf("%d%d",&n,&q);
    for(int i=1;i<=n;i++) size[i]=1;
    for(int i=1;i<=q;i++)
    {
        char opt;
        int x,y;
        cin>>opt;
        scanf("%d%d",&x,&y);
        if(opt=='A') link(x,y);
        else printf("%d\n",query(x,y));
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值