zoukankan      html  css  js  c++  java
  • 题解:「CEOI2017」Chase

    题面 Link

    为了方便,题面中的 (F_i) 在下面描述中改为 (P_i)(V) 改为 (m)

    子任务一: Brute Force

    直接枚举,时间复杂度 (mathcal{O}(n^2 2^n))

    子任务二 / 三: Greedy

    需要优化掉暴力枚举的 (2^n) ,考虑贡献法。

    通过简单手玩,若设 (S_u) 表示与节点 (u) 相邻的所有铁球和,发现每个节点对答案的贡献是 (S_u - F_{lst})(lst) 表示上一个走过节点的编号。

    这样记录每一条链上点所有的贡献,存在 std::set 中,到叶子节点取最大贡献并统计答案即可。

    时间复杂度 (mathcal{O}(n^2 mlog m))

    子任务四: DP

    不妨将一条链 (u ightarrow v) 看成 (u ightarrow p)(p ightarrow v)

    这样就可以通过枚举 (p) 并记录其子树信息的最优解来解决问题了。

    (F(u,i)) 表示用了 (leq i) 次磁铁,从以 (u) 为根的子树中一个节点到 (u) 的路径的贡献的最大值。

    (G(u,i)) 表示用了 (leq i) 次磁铁,从节点 (u) 到子树中一个节点的路径贡献的最大值。

    显然有转移方程:

    [F(u,i) = max{F(u,i - 1), F(v, i), F(v, i - 1) + S_u - P_v} ]

    [G(u,i) = max{G(u,i - 1), G(v, i), G(v, i - 1) + S_v - P_u} ]

    但是 (Ans eq max{F(u,i) + G(v,m - i) , G(u,i) + F(v,m - i)})

    所以对于一个节点,我们需要先记录已经遍历过的子树的最优信息,再与当前子树合并即可。

    时间复杂度 (mathcal{O}(n imes m))

    #include <bits/stdc++.h>
    #define forn(i,s,t) for(register int i=(s);i<=(t);++i)
    using namespace std;
    typedef long long LL;
    const int N = 1e5 + 3, M = 1e2 + 3;
    struct List {
        int dir, nxt;
    } E[N << 1];
    template<typename T>inline T Max(T A, T B) {
        return A > B ? A : B;
    }
    int T[N], cnt;
    inline void Add(int u, int v) {
        E[++cnt].dir = v, E[cnt].nxt = T[u], T[u] = cnt;
    }
    int n, m;
    LL P[N], val[N], F[N][M], G[N][M], Ans;
    LL Fsub[M], Gsub[M];
    // F:: Up Sta G:: Dn Sta
    void dfs(int u, int fa) {
        forn(i, 1, m) F[u][i] = val[u];
        Ans = Max(Ans, val[u]);
    
        for (register int i = T[u]; i; i = E[i].nxt) {
            int v = E[i].dir;
    
            if (v == fa)
                continue ;
    
            dfs(v, u);
            forn(j, 1, m) {
                Fsub[j] = Max(F[v][j], F[v][j - 1] + val[u] - P[v]);
                Gsub[j] = Max(G[v][j], G[v][j - 1] + val[v] - P[u]);
                Ans = Max(Ans, Max(Fsub[j] + G[u][m - j], Gsub[j] + F[u][m - j]));
            }
            forn(j, 1, m) {
                F[u][j] = Max(F[u][j], Max(F[u][j - 1], Fsub[j]));
                G[u][j] = Max(G[u][j], Max(G[u][j - 1], Gsub[j]));
            }
        }
    }
    int main() {
        scanf("%d%d", &n, &m);
    
        if (!m)
            return puts("0"), 0;
    
        forn(i, 1, n) scanf("%lld", P + i);
        forn(i, 2, n) {
            static int u, v;
            scanf("%d%d", &u, &v);
            Add(u, v), Add(v, u);
            val[v] += P[u], val[u] += P[v];
        }
        dfs(1, 0);
        printf("%lld
    ", Ans);
        return 0;
    }
    
  • 相关阅读:
    165. Compare Version Numbers
    164. Maximum Gap
    3、桶排序
    162. Find Peak Element
    160. Intersection of Two Linked Lists
    155. Min Stack
    154. Find Minimum in Rotated Sorted Array II
    153. Find Minimum in Rotated Sorted Array
    Linux/Unix系统编程手册 第二章:基本概念
    Linux/Unix系统编程手册 第一章:历史和标准
  • 原文地址:https://www.cnblogs.com/Ax-Dea/p/14493286.html
Copyright © 2011-2022 走看看