10.9 换根dp
F. Tree with Maximum Cost
问题 - F - Codeforces — Problem - F - Codeforces
换根dp模版题。
和P3478 POI 2008] STA-Station - 洛谷那题差距只在点权。
和P2986 USACO10MAR] Great Cow Gathering G - 洛谷奶牛那题差距在点权和边权。
题意:设顶点为v,最大化∑i=1ndis(i,v)⋅ai\sum_{i=1}^ndis(i,v)⋅a_i∑i=1ndis(i,v)⋅ai。
思路:先让1为顶点,求出∑i=1ndis(i,1)⋅ai\sum_{i=1}^ndis(i,1)⋅a_i∑i=1ndis(i,1)⋅ai。再遍历换根,保存最大值为答案。
换根的公式推理见下:
我们以2,3->4->5->1为例,容易发现以1为根贡献相当于:2+3 +2+3+4 +2+3+4+5。
如果转移5为根,原本5的子树2 3 4 5都变近了,只有它的上子树1变远。
发现改变后的贡献为: 2+3 +2+3+4 +1 这相当于dp[1]-siz[5]+(n-siz[5]);
同理,我们将5转移为4发现: 2+3 +5+1 +1 相当于dp[4]-siz[4]+(n-siz[4])。
我们可以用dfs求出根的子树点权值和,用n减去就是它的上子树点权值和。
故得到换根转移公式: dp[u] = dp[fa] - siz[u] + (sum - siz[u]);
int siz[N]; // i子树的点权和
int dp[N]; // 以i为根的答案
void solve()
{cin >> n;rep(1, i, n){cin >> a[i];sum += a[i];}m = n - 1;while (m--){cin >> x >> y;e[x].emplace_back(y);e[y].emplace_back(x);}auto dfs1 = [&](this auto &&self, int u, int fa, int dep) -> void { // 计算以1为根siz[u] = a[u];for (auto x : e[u]){if (x == fa){continue;}self(x, u, dep + 1);siz[u] += siz[x];dp[1] += siz[x];}};auto dfs2 = [&](this auto &&self, int u, int fa) -> void { // 向子节点换根if (u != 1)dp[u] = dp[fa] - siz[u] + (sum - siz[u]);//换根之后,原根的子树都变小了,上子树变大for (auto x : e[u]){if (x == fa){continue;}self(x, u);}};dfs1(1, 0, 1);dfs2(1, 0);ans = 0;for (int i = 1; i <= n; i++)ans = max(ans, dp[i]);cout << ans;
}