zoukankan      html  css  js  c++  java
  • Luogu P4219 [BJOI2014]大融合

    题意

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

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

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

    ( exttt{Data Range:}1leq n,qleq 10^5)

    题解

    神仙题。

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

    考虑使用 ( exttt{LCT}) 维护虚子树大小,这个与普通的 ( exttt{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
    ",(li)(lct.nd[x].si+1)*(lct.nd[y].si+1));
            }
        }
    }
    
  • 相关阅读:
    [转载]浅谈多态机制的意义及实现
    [转载]浅析Java虚拟机结构与机制
    为什么调用 FragmentPagerAdapter.notifyDataSetChanged() 并不能更新其 Fragment?
    Android-- FragmentStatePagerAdapter分页
    android-点击空白或点击除EditText之外的控件隐藏软键盘
    populating-next-right-pointers-in-each-node
    roman-to-integer
    same-tree
    palindrome-number
    best-time-to-buy-and-sell-stock
  • 原文地址:https://www.cnblogs.com/Karry5307/p/13046640.html
Copyright © 2011-2022 走看看