• Solution 「ZJOI 2018」「洛谷 P4338」历史


    \(\mathscr{Description}\)

      Link.

      给定一棵以 \(1\) 为根的树,点 \(u\) 有非负点权 \(a_u\)

      定义 \(u\) 的一次染色的代价为:路径 \(u\rightarrow 1\) 上染有的不为 \(u\)颜色种类,并把该路径上的所有结点颜色染为 \(u\)

      定义一棵树的染色代价为:初始时所有结点无色,以任意顺序对结点进行染色操作,使得结点 \(u\) 恰好被操作 \(a_u\) 次,能得到的最大操作代价和。

      给出 \(m\) 次操作,每次给定 \(u,k\)\(k>0\),令 \(a_u\leftarrow a_u+k\)。每次修改后求出这棵树的染色代价。

      \(n,m\le4\times10^5\)

    \(\mathscr{Solution}\)

      惯用 trick:染色时的颜色冲突一定可以在 LCA 处不重不漏地统计。对于 \(u\),设其子树内的 \(a\) 之和为 \(s\),自己的权值以及孩子子树和的最大值为 \(x\),显然 \(u\) 处最多冲突

    \[f(s,x)=\begin{cases} s-1,&2x\le s\\ 2(s-x),&2x>s \end{cases} \]

    次,而 \(\sum_u f(s_u,x_u)\) 就是答案。

      奇妙的观察:修改的 \(k>0\),因此修改若在 \(u\) 的最大值子树内且 \(2x_u>s_u\),则一定不会导致 \(f(s_u,x_u)\) 改变。

      因此,可以 LCT 动态维护树形,\(u\) 与其原树孩子 \(v\) 是实边当且仅当 \(2s_u>s_v\)。注意这样的规定虽然导致我们完全不能 access,但是每遇到一条虚边,子树和 \(\times 2\),故任意结点到根的虚边数量恒不超过 \(\mathcal O(\log V)\)。修改时,转一转 splay,讨论一下每条虚边的取舍即可。

      虚边访问次数是 \(\mathcal O(q\log V)\),但 splay 内部是否能套用 LCT 的均摊证明其复杂度呢?或者说有什么巧妙的证明方法呢?求教 qwq。

    \(\mathscr{Code}\)

    /*+Rainybunny+*/
    
    #include <bits/stdc++.h>
    
    #define rep(i, l, r) for (int i = l, rep##i = r; i <= rep##i; ++i)
    #define per(i, r, l) for (int i = r, per##i = l; i >= per##i; --i)
    #ifndef RYBY
    #   undef assert
    #   define assert(expr) // do nothing.
    #endif
    
    typedef long long LL;
    
    inline char fgc() {
        static char buf[1 << 17], *p = buf, *q = buf;
        return p == q && (q = buf + fread(p = buf, 1, 1 << 17, stdin), p == q) ?
          EOF : *p++;
    }
    
    template <typename Tp = int>
    inline Tp rint() {
        Tp x = 0, s = fgc(), f = 1;
        for (; s < '0' || '9' < s; s = fgc()) f = s == '-' ? -f : f;
        for (; '0' <= s && s <= '9'; s = fgc()) x = x * 10 + (s ^ '0');
        return x * f;
    }
    
    template <typename Tp>
    inline void wint(Tp x) {
        if (x < 0) putchar('-'), x = -x;
        if (9 < x) wint(x / 10);
        putchar(x % 10 ^ '0');
    }
    
    const int MAXN = 4e5;
    int n, m, ecnt, head[MAXN + 5];
    struct Edge { int to, nxt; } graph[MAXN * 2 + 5];
    LL ans;
    
    inline void link(const int u, const int v) {
        graph[++ecnt] = { v, head[u] }, head[u] = ecnt;
        graph[++ecnt] = { u, head[v] }, head[v] = ecnt;
    }
    
    struct LinkCutTree {
        int fa[MAXN + 5], ch[MAXN + 5][2];
        LL val[MAXN + 5], asum[MAXN + 5], vsum[MAXN + 5];
    
        inline bool nroot(const int u) {
            return ch[fa[u]][0] == u || ch[fa[u]][1] == u;
        }    
    
        inline void pushup(const int u) {
            asum[u] = asum[ch[u][0]] + asum[ch[u][1]] + val[u] + vsum[u];
        }
    
        inline LL contr(const int u) {
            assert(!nroot(u));
            LL s = asum[u] - asum[ch[u][0]];
            if (ch[u][1]) {
                // assert(asum[ch[u][1]] << 1 > s);
                return s - asum[ch[u][1]] << 1;
            }
            if (val[u] << 1 > s) return s - val[u] << 1;
            return s - 1;
        }
    
        inline void init(const int u) { // specialized.
            int mx = u; asum[u] = val[u];
            for (int i = head[u], v; i; i = graph[i].nxt) {
                if ((v = graph[i].to) != fa[u]) {
                    fa[v] = u, init(v), vsum[u] += asum[v];
                    if (asum[v] > asum[mx]) mx = v;
                }
            }
            asum[u] += vsum[u];
            if (mx != u && asum[mx] << 1 > asum[u]) {
                ch[u][1] = mx, vsum[u] -= asum[mx];
            }
            ans += contr(u);
        }
    
        inline void rotate(const int u) {
            int v = fa[u], w = fa[v], k = ch[v][1] == u;
            fa[u] = w; if (nroot(v)) ch[w][ch[w][1] == v] = u;
            if ((ch[v][k] = ch[u][!k])) fa[ch[v][k]] = v;
            pushup(ch[fa[v] = u][!k] = v), pushup(u);
        }
    
        inline void splay(const int u) {
            for (int v; nroot(u); rotate(u)) {
                if (nroot(v = fa[u])) {
                    rotate(u ^ v ^ ch[v][0] ^ ch[fa[v]][0] ? u : v);
                }
            }
        }
    
        inline void update(const int u, const int k) { // semi-access.
            for (int v = u; v; v = fa[v]) {
                splay(v), ans -= contr(v);
                asum[v] += k, (u == v ? val[v] : vsum[v]) += k;
            }
            if (asum[ch[u][1]] << 1 <= asum[u] - asum[ch[u][0]]) {
                vsum[u] += asum[ch[u][1]], ch[u][1] = 0;
            }
            ans += contr(u);
            for (int v = u, w; (w = fa[v]); v = w) {
                LL s = asum[w] - asum[ch[w][0]];
                if (asum[ch[w][1]] << 1 <= s) {
                    vsum[w] += asum[ch[w][1]], ch[w][1] = 0;
                }
                if (asum[v] << 1 > s) {
                    vsum[w] -= asum[v], ch[w][1] = v;
                }
                ans += contr(w);
            }
        }
    } lct;
    
    int main() {
        n = rint(), m = rint();
        rep (i, 1, n) lct.val[i] = rint();
        rep (i, 2, n) link(rint(), rint());
    
        lct.init(1);
        wint(ans), putchar('\n');
        for (int u, k; m--;) {
            u = rint(), k = rint();
            lct.update(u, k);
            wint(ans), putchar('\n');
        }
        return 0;
    }
    
    
  • 相关阅读:
    [ARC117F]Gateau
    [ARC117D]Miracle Tree
    [loj3504]支配
    [gym102511K]Traffic Blights
    [loj3501]图函数
    [loj3503]滚榜
    [loj3500]矩阵游戏
    [loj2135]幻想乡战略游戏
    [cf720D]Slalom
    [cf1349E]Slime and Hats
  • 原文地址:https://www.cnblogs.com/rainybunny/p/15932856.html
Copyright © 2020-2023  润新知