P4219 [BJOI2014]大融合

传送门

动态维护森林

显然考虑 $LCT$

但是发现询问求的是子树大小,比较不好搞

维护 $sum[x]$ 表示节点 $x$ 的子树大小,$si[x]$ 表示 $x$ 的子树中虚儿子的子树大小和

那么 $pushup$ 可以这样写:

inline void pushup(int x) { sum[x]=sum[c[x][0]]+sum[c[x][1]]+si[x]+1; }

考虑什么时候 $si$ 会变

首先对于 $rotate,splay$ 因为都是对一条实链搞,所以对虚边没影响

然后考虑 $access$ ,发现边的虚实有改变

原本 $x$ 的右儿子变成另一个节点,那么要记得更新

然后 $makeroot$ ,发现我们翻转的是一条实链,所以同样不会对虚边产生影响

然后 $split$ ,调用了 $makeroot,access,splay$ 这些之前都考虑过了

然后 $link$,发现多了一条虚边,所以要记得更新一下

然后 $cut$,因为断的是实边,所以不会改变

那么询问时只要 $makeroot(x),access(y),splay(y)$ ,然后 $y$ 的右儿子就是 $x$ ,输出 $(si[x]+1)*(si[y]+1)$ 即可

具体看代码

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
typedef long long ll;
inline int read()
{
    int x=0,f=1; char ch=getchar();
    while(ch<'0'||ch>'9') { if(ch=='-') f=-1; ch=getchar(); }
    while(ch>='0'&&ch<='9') { x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); }
    return x*f;
}
const int N=2e5+7;
int c[N][2],fa[N],si[N],sum[N];
bool rev[N];//维护的是延时标记
inline void pushup(int x) { sum[x]=sum[c[x][0]]+sum[c[x][1]]+si[x]+1; }
inline void pushdown(int x)
{
    if(!x||!rev[x]) return;
    int &lc=c[x][0],&rc=c[x][1];
    swap(lc,rc); rev[x]=0;
    if(lc) rev[lc]^=1;
    if(rc) rev[rc]^=1;
}
inline void rever(int x) { rev[x]^=1; pushdown(x); }
inline bool notroot(int x) { return (c[fa[x]][0]==x)|(c[fa[x]][1]==x); }
inline void rotate(int x)
{
    int y=fa[x],z=fa[y],d=(c[y][1]==x);
    if(notroot(y)) c[z][c[z][1]==y]=x;
    fa[x]=z; fa[y]=x; fa[c[x][d^1]]=y;
    c[y][d]=c[x][d^1]; c[x][d^1]=y;
    pushup(y); pushup(x);
}
void push_tag(int x)
{
    if(notroot(x)) push_tag(fa[x]);
    else pushdown(x);
    pushdown(c[x][0]); pushdown(c[x][1]);
}
inline void splay(int x)
{
    push_tag(x);
    while(notroot(x))
    {
        int y=fa[x],z=fa[y];
        if(notroot(y))
        {
            if(c[z][0]==y ^ c[y][0]==x) rotate(x);
            else rotate(y);
        }
        rotate(x);
    }
}
inline void access(int x)
{
    for(int y=0;x;y=x,x=fa[x])
    {
        splay(x); si[x]+=(sum[c[x][1]]-sum[y]);//记得更新si
        c[x][1]=y; pushup(x);
    }
}
inline void makeroot(int x) { access(x); splay(x); rever(x); }
inline void split(int x,int y) { makeroot(x); access(y); splay(y); }
inline void link(int x,int y) { split(x,y); fa[x]=y; si[y]+=sum[x];/*更新si*/ pushup(y); }
inline void query(int x,int y)
{
    split(x,y);//和makeroot(x),access(y),splay(y)是同样的写法
    printf("%lld
",1ll*(si[x]+1)*(si[y]+1));
}
int n,m;
int main()
{
    int a,b; char s[7];
    n=read(); m=read();
    for(int i=1;i<=n;i++) sum[i]=1;
    while(m--)
    {
        scanf("%s",s); a=read(),b=read();
        if(s[0]=='A') link(a,b);
        else query(a,b);
    }
    return 0;
}
原文地址:https://www.cnblogs.com/LLTYYC/p/10658056.html