• [BJOI2014][BZOJ4530][洛谷P4219]大融合(LCT)


    Solution

    • \(LCT\),对每个节点 \(u\) 维护两个信息:\(s[u], si[u]\)
    • \(sze[u]\) 为实子树的大小,即 \(splay\) 上的子树大小
    • \(s[u]=si[u]+sze[u]\)
    • \(si[u]\) :虚子树的大小之和,即所有虚儿子的 \(s\) 之和
    • 如果 \(u\) 是所在重链的根,那么 \(s[u]\)\(u\) 原树(即题目中所给的树结构)上的子树大小
    • 对于边 \((x,y)\),如果执行 \(split(x,y)\),那么 \((si[x]+1) * (si[y] + 1)\) 即答案
    • 考虑如何维护 \(s\)\(si\)
    • 显然初始所有 \(s[u] = 1,si[u]=0\)
    • 改变虚儿子会改变 \(si[u]\),改变实儿子和虚儿子都会改变 \(s[u]\)
    • 也就是说 \(rotate,access,link\) 都会改变至少一个信息
    • 对于 \(rotate\),和维护其它信息一样,旋转之后 \(upt/pushup\) 即可
    • 对于 \(access\),给出如下代码:
    inline void access(int x)
    {
        for (int y = 0; x; y = x, x = fa[x])
        {
            splay(x);
            si[x] += s[rc[x]];
            si[x] -= s[y];
            rc[x] = y;
            if (y) fa[y] = x;
        }
    }
    
    • 可以看出,\(x\) 原来的实儿子变虚儿子,\(y\) 所在重链的根由虚儿子变实儿子,所以要修改 \(si[x]\),但是 \(s[x]\) 不用修改,因为两个儿子虚实互换,实儿子 \(+\) 虚儿子大小不会改变
    • 对于 \(link\),给出如下代码
    inline void link(int x, int y)
    {
        makeroot(x);
        access(y);
        splay(y);
        fa[x] = y;
        si[y] += s[x];
        upt(y);
    }
    
    • 可以看出,\(y\) 多出来了一个实儿子 \(x\),所以要修改 \(si[y]\)\(s[y]\)
    • 重点是,要先 \(access(y), splay(y)\),因为 \(si[y]\)\(s[y]\) 的修改会导致 \(y\) 的祖先信息发生变化,所以让 \(y\) 和根合到一个 \(splay\) 中并将 \(y\) 转到根,即让 \(fa[y] = 0\),就没有祖先受到影响了
    • 现在问题来了,刚才的 \(access\) 中修改了 \(si\) ,是否也要考虑对祖先的影响呢?其实不用,观察这段代码可知,每次都 \(splay(x)\),也就是不用考虑对同一 \(splay\) 中的祖先的影响了。而且只修改 \(si[x]\),对不同 \(splay\) 中的祖先没有任何影响

    Code

    #include <bits/stdc++.h>
    
    using namespace std;
    
    template <class t>
    inline void read(t & res)
    {
        char ch;
        while (ch = getchar(), !isdigit(ch));
        res = ch ^ 48;
        while (ch = getchar(), isdigit(ch))
        res = res * 10 + (ch ^ 48);
    }
    
    const int e = 1e5 + 5;
    int n, m, lc[e], s[e], si[e], rc[e], rev[e], fa[e], q[e];
    
    inline bool which(int x)
    {
        return rc[fa[x]] == x;
    }
    
    inline bool isroot(int x)
    {
        return !fa[x] || (lc[fa[x]] != x && rc[fa[x]] != x);
    }
    
    inline void pushdown(int x)
    {
        if (rev[x])
        {
            if (lc[x]) rev[lc[x]] ^= 1;
            if (rc[x]) rev[rc[x]] ^= 1;
            swap(lc[x], rc[x]);
            rev[x] = 0;
        }
    }
    
    inline void upt(int x)
    {   
        s[x] = s[lc[x]] + s[rc[x]] + 1 + si[x];
    }
    
    inline void rotate(int x)
    {
        int y = fa[x], z = fa[y];
        pushdown(y);
        pushdown(x);
        int b = (lc[y] == x ? rc[x] : lc[x]);
        if (z && !isroot(y)) (lc[z] == y ? lc[z] : rc[z]) = x;
        fa[x] = z;
        fa[y] = x;
        if (b) fa[b] = y;
        if (lc[y] == x)
        {
            rc[x] = y;
            lc[y] = b;
        }
        else
        {
            lc[x] = y;
            rc[y] = b;
        }
        upt(y);
        upt(x);
    }
    
    inline void splay(int x)
    {
        int w;
        q[w = 1] = x;
        for (int y = x; !isroot(y); y = fa[y]) q[++w] = fa[y];
        for (int i = w; i >= 1; i--) pushdown(q[i]);
        while (!isroot(x))
        {
            if (!isroot(fa[x]))
            {
                if (which(x) == which(fa[x])) rotate(fa[x]);
                else rotate(x);
            }
            rotate(x);
        }
    }
    
    inline void access(int x)
    {
        for (int y = 0; x; y = x, x = fa[x])
        {
            splay(x);
            si[x] += s[rc[x]];
            si[x] -= s[y];
            rc[x] = y;
            if (y) fa[y] = x;
        }
    }
    
    inline void makeroot(int x)
    {
        access(x);
        splay(x);
        rev[x] ^= 1;
    }
    
    inline void link(int x, int y)
    {
        makeroot(x);
        access(y);
        splay(y);
        fa[x] = y;
        si[y] += s[x];
        upt(y);
    }
    
    inline void split(int x, int y)
    {
        makeroot(x);
        access(y);
        splay(y);
    }
    
    int main()
    {
        int i, x, y;
        char ch;
        read(n); read(m);
        for (i = 1; i <= n; i++) s[i] = 1;
        while (m--)
        {
            while (ch = getchar(), !isalpha(ch));
            read(x);
            read(y);
            if (ch == 'A') link(x, y);
            else
            {
                split(x, y);
                printf("%lld\n", 1ll * (si[x] + 1) * (si[y] + 1));
            }
        }
        return 0;
    }
    
  • 相关阅读:
    网络七层协议
    Sizeof与Strlen的区别与联系
    Java面试宝典(说说&和&&的区别)
    Java se基础(类的属性及关键字)
    了解java中垃圾回收机制
    MYSQL常用函数(系统信息函数)
    MYSQL常用函数(类型转化函数)
    MYSQL常用函数(格式化函数)
    MYSQL常用函数(控制流函数)
    【CF-1362】B. Johnny and His Hobbies
  • 原文地址:https://www.cnblogs.com/cyf32768/p/12196936.html
Copyright © 2020-2023  润新知