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

13 篇文章 0 订阅

Description


小强要在N个孤立的星球上建立起一套通信系统。这套通信系统就是连接N个点的一个树。这个树的边是一条一条添加上去的。在某个时刻,一条边的负载就是它所在的当前能够联通的树上路过它的简单路径的数量。

对于40%的数据,N,Q≤1000
对于100%的数据,1≤N,Q≤100000

Solution


所求答案就是给定边两端子树size的乘积,由于我不cai会de在kou线jiao做写了一发lct维护子树size

考虑用lct维护加边的操作
用siz[x]记录点x所有虚边连向的儿子所在子树的大小(绕
用size[x]记录点x在splay上左儿子size+右儿子size+虚儿子+1
容易发现当我们access(x)的时候它的子树大小就是siz[x]+1,当我们mroot(x)的时候它的子树大小则是size[x]
写的时候注意一下access操作和link操作的变化就行了

Code


#include <stdio.h>
#include <string.h>
#include <algorithm>
#define rep(i,st,ed) for (int i=st;i<=ed;++i)

const int N=200005;

struct treeNode {
    int fa,son[2],size,siz,rev,is_root;
} t[N];

int read() {
    int x=0,v=1; char ch=getchar();
    for (;ch<'0'||ch>'9';v=(ch=='-')?(-1):(v),ch=getchar());
    for (;ch<='9'&&ch>='0';x=x*10+ch-'0',ch=getchar());
    return x*v;
}

void push_up(int x) {
    t[x].size=t[t[x].son[0]].size+t[t[x].son[1]].size+t[x].siz+1;
}

void push_down(int x) {
    if (!t[x].rev) return; 
    t[x].rev=0; std:: swap(t[x].son[0],t[x].son[1]);
    if (t[x].son[0]) t[t[x].son[0]].rev^=1;
    if (t[x].son[1]) t[t[x].son[1]].rev^=1;
}

void rotate(int x) {
    int y=t[x].fa; int z=t[y].fa;
    int k=t[y].son[1]==x;
    t[y].son[k]=t[x].son[!k]; if (t[x].son[!k]) t[t[x].son[!k]].fa=y;
    t[y].fa=x; t[x].son[!k]=y;
    t[x].fa=z;
    if (t[y].is_root) {
        t[y].is_root=0;
        t[x].is_root=1;
    } else t[z].son[t[z].son[1]==y]=x;
    push_up(y); push_up(x);
}

void remove(int x) {
    if (!t[x].is_root) remove(t[x].fa);
    push_down(x);
}

void splay(int x) {
    remove(x);
    while (!t[x].is_root) {
        int y=t[x].fa; int z=t[y].fa;
        if (!t[y].is_root) {
            if ((t[z].son[1]==y)^(t[y].son[1]==x)) rotate(x);
            else rotate(y);
        }
        rotate(x);
    }
}

void access(int x) {
    int y=0;
    while (x) {
        splay(x);
        t[y].is_root=0;
        t[t[x].son[1]].is_root=1;
        t[x].siz+=t[t[x].son[1]].size;
        t[x].son[1]=y;
        t[x].siz-=t[y].size;
        push_up(x);
        y=x; x=t[x].fa;
    }
}

void mroot(int x) {
    access(x); splay(x); t[x].rev^=1;
}

void link(int x,int y) {
    mroot(x); mroot(y); t[x].fa=y; t[y].siz+=t[x].size; push_up(y);
}

int main(void) {
    int n=read(),q=read();
    rep(i,1,n) t[i].size=t[i].is_root=1;
    for (;q--;) {
        char opt=getchar();
        for (;opt!='A'&&opt!='Q';opt=getchar());
        int x=read(),y=read();
        if (opt=='A') {
            link(x,y);
        } else {
            mroot(x); access(y); splay(x);
            printf("%lld\n", 1LL*(t[y].siz+1)*(t[x].size-t[y].siz-1));
        }
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值