[BJOI2014]大融合

题面理解很容易,求树上两颗树节点数之和。

这里需要lct维护虚子树信息。

对于每个节点,我们记录认的儿子和不认的儿子大小之和(s),和不认的儿子大小和(sx)。

在access中,x节点换儿子时,需要对x的sx值加上前任儿子值,在减去现任儿子值。

代码:

#include<cstdio>
#include<algorithm>
using namespace std;
#define N 100500
#define ll long long
ll n,m;
ll ch[N][2],fa[N],s[N],sx[N];
bool rt[N],res[N];
void update(ll x)
{
    s[x] = sx[x]+s[ch[x][0]]+s[ch[x][1]]+1;
}
void reser(ll x)
{
    res[x]^=1;
    swap(ch[x][0],ch[x][1]);
}
void pushdown(ll x)
{
    if(res[x])
    {
        res[x]=0;
        reser(ch[x][0]);
        reser(ch[x][1]);
    }
}
void down(ll x)
{
    if(!rt[x])down(fa[x]);
    pushdown(x);
}
void rotate(ll x)
{
    ll y = fa[x] , k = (ch[y][1]==x);
    if(rt[y])rt[y]=0,rt[x]=1;
    else ch[fa[y]][ch[fa[y]][1]==y] = x;
    fa[x] = fa[y];
    ch[y][k] = ch[x][!k],fa[ch[x][!k]] = y;
    ch[x][!k] = y,fa[y] = x;
    update(y),update(x);
}
void splay(ll x)
{
    down(x);
    while(!rt[x])
    {
        ll y = fa[x], z = fa[y];
        if(!rt[y])
            ((ch[y][1]==x)^(ch[z][1]==y))?rotate(x):rotate(y);
        rotate(x);
    }
}
void access(ll x)
{
    ll y = 0;
    while(x)
    {
        splay(x);
        sx[x]+=s[ch[x][1]];
        sx[x]-=s[y];
        rt[ch[x][1]] = 1,rt[y] = 0;
        ch[x][1] = y;
        update(x);
        y=x,x=fa[x];
    }
}
void mtr(ll x)
{
    access(x);
    splay(x);
    reser(x);
}
void link(ll x,ll y)
{
    mtr(x);
    access(y);
    splay(y);
    fa[x] = y;
    sx[y]+=s[x];
    update(y);
}
int main()
{
    scanf("%lld%lld",&n,&m);
    for(ll i=1;i<=n;i++)rt[i]=1,s[i]=1;
    char cc[2];
    ll u,v;
    for(ll i=1;i<=m;i++)
    {
        scanf("%s%lld%lld",cc,&u,&v);
        if(cc[0]=='Q')
        {
            mtr(u);
            access(v);
            splay(u);
            printf("%lld
",(s[u]-s[v])*(s[v]));
        }else link(u,v);
    }
    return 0;
}
原文地址:https://www.cnblogs.com/LiGuanlin1124/p/9646876.html