[洛谷 P2986 USACO10MAR] Great Cow Gathering G
题目链接
大致题意:
有一颗树,你可以选定一个根节点,这个根节点的代价是∑c[i]∗dis[i],c[i]表示点i的点权值,dis[i]表示点i到根节点的距离
解题思路:
二次扫描换根法
操作1:
先处理出以1为根节点的时候的代价
定义状态方程: f[i]表示i子树中所有点到i的代价
转移方程:f[u] += f[v] + w * cnt[v] (cnt[i]表示i的子树中所有点权和)
操作2:
重新定义状态方程: f[i]表示以i为根节点,书中所有点到i的代价
转移方程:f[v] = f[u] - cnt[v] * w + (sum - cnt[v]) * w (sum表示所有的点权和)
注意开long long
AC代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e5 + 10;
int n, m;
ll c[N], cnt[N], sum; //cnt[i] 表示i的子树中所有点的权值和
vector<pair<int, int>>e[N];
ll f[N], res; //f[i]第一次表示i子树中所有点到i的代价 第二次表示所有点到i的代价
void dfs1(int u, int fa) {
cnt[u] = c[u];
for (auto& op : e[u]) {
int v = op.first, w = op.second;
if (v == fa)continue;
dfs1(v, u);
cnt[u] += cnt[v];
f[u] += f[v] + w * cnt[v];
}
}
void dfs2(int u, int fa) {
for (auto& op : e[u]) {
int v = op.first, w = op.second;
if (v == fa)continue;
f[v] = f[u] - cnt[v] * w + (sum - cnt[v]) * w;
res = min(res, f[v]);
dfs2(v, u);
}
}
int main(void)
{
cin >> n;
for (int i = 1; i <= n; ++i)cin >> c[i], sum += c[i];
for (int i = 1; i < n; ++i) {
int a, b, l; cin >> a >> b >> l;
e[a].push_back({ b,l });
e[b].push_back({ a,l });
}
dfs1(1, -1);
res = f[1];
dfs2(1, -1);
cout << res << endl;
return 0;
}