P4219 [BJOI2014]大融合

题目

P4219 [BJOI2014]大融合

\(LCT\)虚边处理模板题

经过之前做过的\(LCT\),我们已经对于\(LCT\)树上动态删(添)边查询链非常熟悉了

经过以前做的树形动规,显然x与y各自子树乘积(口胡的)

所以这题需要动态维护子树,\(LCT\)中我们需要维护虚边,一步一步来分析

数组\(sum_i\)\(i\)\(LCT\)(多棵\(splay\)下不分虚实边)里的子树大小,\(isum_i\)\(i\)\(LCT\)中虚儿子的子树和

先来考虑查询\((x,y)\)\(split(x,y),printf("%d\n",(isum[x]+1)*(isum[y]+1))\),我们拉\(x,y\)这条链,此时原树子树就是虚边(画个图就懂了吧)

现在思考什么时候我们需要更新(建议自己\(yy\)一下,对着函数很好想的),只需要思考几个函数,因为其他的差不多都是调用这几个

Update

inline void Update(LL x){
    sum[x]=sum[son[x][0]]+sum[son[x][1]]+isum[x]+1;
}

\(Splay(x)\):反正是单独一棵\(splay\)上旋不需要管
\(Access(x)\):思考拉链的过程中间是变化了很多次虚实边的,一加一减即可

inline void Access(LL x){
    for(LL y=0;x;y=x,x=fa[x]){
        Splay(x); isum[x]+=sum[son[x][1]];
        son[x][1]=y; isum[x]-=sum[son[x][1]];
        Update(x);
    }
}

\(Link(x,y)\):我们平时是\(x\)变成原根(成为该\(splay\)深度最小的引边)作为\(y\)的轻儿子
但会影响到很多值(\(y\)位于很多结点的下方),如果往上爬会特别麻烦,我们把\(y\)旋到这\(LCT\)的根就好了

inline void Link(LL x,LL y){
    Makeroot(x),Access(y),Splay(y);
    fa[x]=y;
    isum[y]+=sum[x];
    Update(y);
}

\(Delet(x,y)\):删的是实边不需要管

My complete code

#include<cstdio>
#include<cstring>
#include<iostream>
#include<string>
#include<algorithm>
using namespace std;
typedef int LL;
const LL maxn=1e6;
inline LL Read(){
    LL x(0),f(1);char c=getchar();
    while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
    while(c>='0'&&c<='9')x=(x<<3)+(x<<1)+c-'0',c=getchar();
    return x*f;
}
LL n,m;
LL sum[maxn],isum[maxn],son[maxn][2],fa[maxn],r[maxn],sta[maxn];

inline void Update(LL x){
    sum[x]=sum[son[x][0]]+sum[son[x][1]]+isum[x]+1;
}
inline bool Notroot(LL x){
    return son[fa[x]][0]==x||son[fa[x]][1]==x;
}
inline void Pushr(LL x){
    swap(son[x][0],son[x][1]);r[x]^=1;
}
inline void Pushdown(LL x){
    if(r[x]){
        if(son[x][0]) Pushr(son[x][0]);
        if(son[x][1]) Pushr(son[x][1]);
        r[x]=0;
    }
}
inline void Rotate(LL x){
    LL y(fa[x]),z(fa[y]),lz(son[y][1]==x);
    if(Notroot(y)) son[z][son[z][1]==y]=x; fa[x]=z;
    son[y][lz]=son[x][lz^1];
    if(son[y][lz]) fa[son[y][lz]]=y;
    son[x][lz^1]=y; fa[y]=x; 
    Update(y),Update(x);
}
inline void Splay(LL x){
    LL y(x),top(0);
    sta[++top]=y;
    while(Notroot(y)) sta[++top]=y=fa[y];
    while(top) Pushdown(sta[top--]);
    while(Notroot(x)){
        y=fa[x];
        if(Notroot(y)){
            LL z(fa[y]);
            if(((son[z][1]==y)^(son[y][1]==x))) Rotate(y);
            else Rotate(x);
        }
        Rotate(x);
    }
}
inline void Access(LL x){
    for(LL y=0;x;y=x,x=fa[x]){
        Splay(x); isum[x]+=sum[son[x][1]];
        son[x][1]=y; isum[x]-=sum[son[x][1]];
        Update(x);
    }
}
inline void Makeroot(LL x){
    Access(x),Splay(x),Pushr(x);
}
inline void Split(LL x,LL y){
    Makeroot(x),Access(y),Splay(y);
}
inline void Link(LL x,LL y){
    Makeroot(x),Access(y),Splay(y);
    fa[x]=y;
    isum[y]+=sum[x];
    Update(y);
}
int main(){
    n=Read(),m=Read();
    while(m--){
        char ch; scanf(" %c",&ch);
        LL x(Read()),y(Read());
        if(ch=='A'){
            Link(x,y);
        }else{
            Split(x,y);
            printf("%d\n",(isum[x]+1)*(isum[y]+1));
        }
    }
}

转载于:https://www.cnblogs.com/y2823774827y/p/10324952.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值