Luogu P4219 [BJOI2014]大融合

题意

给定一个 \(n\) 个点的无边无向图,有 \(q\) 次操作,每次操作分为以下两种:

A x y:将 \(x\)\(y\) 连边,保证 \(x\)\(y\) 不连通。

Q x y:询问图中有多少条路径经过 \((x,y)\)

\(\texttt{Data Range:}1\leq n,q\leq 10^5\)

题解

神仙题。

注意到询问的答案其实是以 \(x\) 为根的子树大小与以 \(y\) 为根的子树大小的乘积。

考虑使用 \(\texttt{LCT}\) 维护虚子树大小,这个与普通的 \(\texttt{LCT}\) 在写法上有一些差别。

第一个是在 access 的时候:(其中 \(sz\) 是子树大小,\(si\) 是虚子树大小)

inline void access(ll x)
{
    for(register int i=0;x;x=nd[i=x].fa)
    {
        splay(x),nd[x].si+=nd[rs].sz,nd[x].si-=nd[rs=i].sz,update(x);
    }
}

access 的时候,原来连接自己与右儿子的边由实的变成了虚的,而新的边由虚的变成了实的,所以这里要统计一下贡献。

第二个是在 link 的时候:

inline void link(ll x,ll y)
{
    split(x,y),nd[nd[x].fa=y].si+=nd[x].sz,update(y);
}

首先把 \(x\)\(y\) 的链抠出来全部变成实链,因为一个点连到儿子的的只能有一条是实链,所以 \((x,y)\) 是虚链,要统计一次贡献。

然后答案就是 \((si_x+1)(si_y+1)\),就成了。

代码

#include<bits/stdc++.h>
using namespace std;
typedef int ll;
typedef long long int li;
const ll MAXN=2e5+51;
ll n,qcnt,x,y;
char ch;
inline ll read()
{
    register ll num=0,neg=1;
    register char ch=getchar();
    while(!isdigit(ch)&&ch!='-')
    {
        ch=getchar();
    }
    if(ch=='-')
    {
        neg=-1;
        ch=getchar();
    }
    while(isdigit(ch))
    {
        num=(num<<3)+(num<<1)+(ch-'0');
        ch=getchar();
    }
    return num*neg;
}
namespace LCT{
    struct Node{
        ll fa,rv,sz,si;
        ll ch[2];
    };
    struct LinkCutTree{
        Node nd[MAXN];
        ll st[MAXN];
        #define ls nd[x].ch[0]
        #define rs nd[x].ch[1]
        inline bool nroot(ll x)
        {
            return nd[nd[x].fa].ch[0]==x||nd[nd[x].fa].ch[1]==x;
        }
        inline void update(ll x)
        {
            nd[x].sz=nd[ls].sz+nd[rs].sz+nd[x].si+1;
        }
        inline void reverse(ll x)
        {
            swap(ls,rs),nd[x].rv^=1;
        }
        inline void spread(ll x)
        {
            if(nd[x].rv)
            {
                ls?reverse(ls):(void)(1),rs?reverse(rs):(void)(1);
                nd[x].rv=0;
            }
        }
        inline void rotate(ll x)
        {
            ll fa=nd[x].fa,gfa=nd[fa].fa;
            ll dir=nd[fa].ch[1]==x,son=nd[x].ch[!dir];
            if(nroot(fa))
            {
                nd[gfa].ch[nd[gfa].ch[1]==fa]=x;
            }
            nd[x].ch[!dir]=fa,nd[fa].ch[dir]=son;
            if(son)
            {
                nd[son].fa=fa;
            }
            nd[fa].fa=x,nd[x].fa=gfa,update(fa);
        }
        inline void splay(ll x)
        {
            ll fa=x,gfa,cur=0;
            st[++cur]=fa;
            while(nroot(fa))
            {
                st[++cur]=fa=nd[fa].fa;
            }
            while(cur)
            {
                spread(st[cur--]);
            }
            while(nroot(x))
            {
                fa=nd[x].fa,gfa=nd[fa].fa;
                if(nroot(fa))
                {
                    rotate((nd[fa].ch[0]==x)^(nd[gfa].ch[0]==fa)?x:fa);
                }
                rotate(x);
            }
            update(x);
        }
        inline void access(ll x)
        {
            for(register int i=0;x;x=nd[i=x].fa)
            {
                splay(x),nd[x].si+=nd[rs].sz,nd[x].si-=nd[rs=i].sz,update(x);
            }
        }
        inline void makeRoot(ll x)
        {
            access(x),splay(x),reverse(x);
        }
        inline void split(ll x,ll y)
        {
            makeRoot(x),access(y),splay(y);
        }
        inline void link(ll x,ll y)
        {
            split(x,y),nd[nd[x].fa=y].si+=nd[x].sz,update(y);
        }
        #undef ls
        #undef rs
    };
}
LCT::LinkCutTree lct;
int main()
{
    n=read(),qcnt=read();
    for(register int i=1;i<=n;i++)
    {
        lct.nd[i].sz=1;
    }
    for(register int i=0;i<qcnt;i++)
    {
        cin>>ch,x=read(),y=read();
        if(ch=='A')
        {
            lct.link(x,y);
        }
        if(ch=='Q')
        {
            lct.split(x,y);
            printf("%lld\n",(li)(lct.nd[x].si+1)*(lct.nd[y].si+1));
        }
    }
}
posted @ 2020-06-04 21:34  Karry5307  阅读(126)  评论(0编辑  收藏  举报