zoukankan      html  css  js  c++  java
  • 洛谷 P4332 [SHOI2014]三叉神经树 题解

    一、题目:

    洛谷原题

    二、思路:

    这道题怎么说呢?只能说有点意思,让我第一次见识了LCT怎么应用。

    首先一个非常明显的性质,就是比如我现在修改了某个叶子结点,记为 (leaf),那么因此而状态发生改变的点一定是从 (leaf) 向上的连续区间。所以我们自然而然能想到两种数据结构,一种是树链剖分,另一种就是LCT,因为这两种都可以较好地维护树链的信息。

    这题用LCT怎么做呢?难点就在于找出每次的连续区间的终点在哪?当然,由于连续区间的性质,我们立刻就能想到二分答案。时间复杂度 (O(nlog^2 n))

    其实,我们还可以通过维护一些信息来降低一下时间复杂度。具体来说,我们每次打通 (leaf) 到根的路径,即 (mathbb{access}(leaf))。这样一来,(leaf) 到根的所有节点就都在一棵 Splay 中了。我们在这棵 Splay 的每个节点中维护两个信息:(id[x,1])(id[x,2]),分别代表Splay(x) 子树的、原树中最深的、(sum) 不为 (1)(2) 的节点编号。

    具体怎么维护呢?请配合注释看代码。

    三、代码:

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    
    using namespace std;
    #define FILEIN(s) freopen(s".in", "r", stdin);
    #define FILEOUT(s) freopen(s".out", "w", stdout)
    #define mem(s, v) memset(s, v, sizeof s)
    
    inline int read(void) {
        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 * 10 + ch - '0'; ch = getchar(); }
        return f * x;
    }
    
    const int maxn = 500005 * 3;
    
    int n, m, son[maxn][2], id[maxn][3], fa[maxn], add[maxn], val[maxn], sum[maxn], tot;
    int head[maxn];
    
    struct Edge {
        int y, next;
        Edge() {}
        Edge(int _y, int _next) : y(_y), next(_next) {}
    }e[maxn << 1];
    
    inline void connect(int x, int y) {
        e[++ tot] = Edge(y, head[x]);
        head[x] = tot;
    }
    
    inline void update(int o) { // 由于Splay结点的中序遍历代表原树的一条从上到下的路径,因此我们必须先看右儿子,再看它自己,最后看它的左儿子。
        id[o][1] = id[son[o][1]][1];
        id[o][2] = id[son[o][1]][2];
        if (!id[o][1]) {
            if (sum[o] != 1) id[o][1] = o;
            else id[o][1] = id[son[o][0]][1];
        }
        if (!id[o][2]) {
            if (sum[o] != 2) id[o][2] = o;
            else id[o][2] = id[son[o][0]][2];
        }
    }
    
    inline void change(int o, int x) {
        sum[o] += x; val[o] = sum[o] > 1;
        swap(id[o][1], id[o][2]); // 修改操作一定只对sum值全部为1或2的区间进行,因此我们只需交换id即可。
        add[o] += x;
    }
    
    inline void pushdown(int o) {
        if (add[o]) {
            if (son[o][0]) change(son[o][0], add[o]);
            if (son[o][1]) change(son[o][1], add[o]);
            add[o] = 0;
        }
    }
    
    inline bool isroot(int x) {
        return son[fa[x]][0] != x && son[fa[x]][1] != x;
    }
    
    inline int get(int x) { return son[fa[x]][1] == x; }
    
    inline void rotate(int x) {
        int y = fa[x], z = fa[y], k = get(x);
        pushdown(y); pushdown(x);
        if (!isroot(y)) son[z][son[z][1] == y] = x;
        son[y][k] = son[x][k ^ 1]; fa[son[y][k]] = y; fa[y] = x;
        son[x][k ^ 1] = y; fa[x] = z;
        update(y); update(x);
    }
    
    void correct(int x) {
        if (!isroot(x)) correct(fa[x]);
        pushdown(x);
    }
    
    inline void splay(int x) {
        correct(x);
        for (int f = fa[x]; !isroot(x); rotate(x), f = fa[x])
            if (!isroot(f))
                rotate(get(x) == get(f) ? f : x);
    }
    
    inline void access(int x) {
        int z = x;
        for (int y = 0; x; y = x, x = fa[x]) {
            splay(x);
            son[x][1] = y;
            update(x);
        }
        splay(z);
    }
    
    void dfs(int x, int fa) {
        sum[x] = 0;
        for (int i = head[x], y; i; i = e[i].next) {
            y = e[i].y;
            if (y == fa) continue;
            dfs(y, x);
            sum[x] += val[y];
        }
        if (x <= n) val[x] = sum[x] > 1;
    }
    
    int main() {
        n = read();
        int x;
        for (int i = 1; i <= n; ++ i) {
            for (int j = 1; j <= 3; ++ j) {
                x = read(); fa[x] = i;
                connect(x, fa[x]); connect(fa[x], x); 
            }
        }
        for (int i = n + 1; i <= n * 3 + 1; ++ i) val[i] = read();
        dfs(1, 0);
        int ans = val[1];
        m = read(); 
        while (m --) {
            int leaf = read(), x = fa[leaf];
            int addtag = val[leaf] ? -1 : 1;
            access(x); 
            int w = id[x][val[leaf] ? 2 : 1];
            if (w) {
                splay(w);
                change(son[w][1], addtag);
                sum[w] += addtag; val[w] = sum[w] > 1; update(w); // 由于w的儿子的id发生了变化,注意要update一下,当然换成splay(w)也是可以的。
            }
            else ans ^= 1, change(x, addtag); // 特殊判断w不存在的情况。
            val[leaf] ^= 1;
            printf("%d
    ", ans);
        }
        return 0;
    }
    
  • 相关阅读:
    c# webapi无法获取Session值问题解决
    深入理解java虚拟机之自动内存管理机制笔记
    数据结构总结1
    疯人院之语言、编码、计算机

    集线器/交换机
    什么是DOM?DOM和JavaScript的关系 [web开发]
    JSON轻量级的数据交换格式
    天问宇宙学第一课
    C++基础知识
  • 原文地址:https://www.cnblogs.com/little-aztl/p/14901128.html
Copyright © 2011-2022 走看看