题目描述
The architectural structure of the college is strange, but the rule is that there is only one simple path between every two classrooms. Now the battle between Class A and Class F broke out. As a support staff of Class F, you have to go to every fight in time to help out. With the help of icebound, Class F know the probability of each classroom being attacked. So we define an important degree for each classroom. Now ask you to find a classroom x in n classrooms as your resting base which has the minimum F(x). F(x) = ∑ w(i) × d(x, i), d is the distance between x and i.
输入
he first line is an integer n,which is the number of classrooms.
Then n-1 lines follow. Each line has three numbers x,y,z. There is a road of z meters between x and y.
The last line contains n numbers. The i‑th number w(i) is the important degree of the classroom.
2 ≤ n ≤ 5 × 105, 0 ≤ z, wi ≤ 1000,1 ≤ x, y ≤ n
输出
Output a line with an integer, representing the minimum F(x).
样例输入 Copy
5 1 2 1 2 3 1 2 4 1 3 5 6 2 3 1 8 7
样例输出 Copy
60
题意:给出一个树,有边权,有节点的重量,让你求g[x]的最小值,g[x] = ∑ ( w[i] * d(i,x) ) 。w[i] 为除x外其他一些节点重量,d(i,x) 为 i 到 x 的距离。
思路:说实话,比赛时,不会写,队友强,用树形dp,递推 贡献值。
树形dp. 首先随机选取一个根, 然后预处理出f[i]表示i的子树上所有点的权值和, g[i]表示i的整颗子树对当前i的贡献
转移 f[i]=w[i]+∑j是i的儿子 f[j]f[i]=w[i]+∑j是i的儿子 f[j] , g[i]=∑j是i的儿子 (g[j]+f[j]∗dis(i,j))g[i]=∑j是i的儿子 (g[j]+f[j]∗dis(i,j))
其中 w[i] 表示i点的权值
然后从根再做一次dfs, 不断将父亲节点当做子节点的子树并将贡献向下传递给子节点即可
代码:
#include<bits/stdc++.h>
using namespace std;
#define ll unsigned long long
#define Max 500010
int w[Max],f[Max];
bool book[Max];
ll g[Max];
struct node{
int y,z;
};
vector<node> v[Max];
int dfs(int u)
{
book[u] = true;
int sum = w[u];
for(int i = 0;i<v[u].size();i++){
node tt = v[u][i];
int v = tt.y;
if(!book[v]){
int k = dfs(v);
sum += k;
g[u] += g[v] + (ll) k * tt.z;
}
}
return f[u] = sum;
}
ll dfs1(int u,int fw,ll fg) // 节点u,fw为当初以1为根节点的树,u为子树时,除u为子树的结点重量和之外的所有结点重量之和
// fg 为 fw 对 节点u的贡献值。
{
ll Ma = g[u]+fg;
book[u] = true;
for(int i = 0;i<v[u].size();i++){
node tt = v[u][i];
int v = tt.y;
if(!book[v]){
Ma = min(Ma,dfs1(v,fw + f[u]-f[v],fg + g[u]-g[v]-(ll)f[v]*tt.z +(ll)(fw+f[u]-f[v])*tt.z));
} // fg + g[u]-g[v]-(ll)(f[v]*tt.z) 为当初以1为根节点时,v为根节点的子树 时,除v为根节点的子树和 u本身节点外,所有节点对
// 所有节点对u的贡献值;(fw+f[u]-f[v])*tt.z)) (fw+f[u]-f[v])为除 v 为根节点子树外的所有权值。乘于tt.z 只是这一段的权重。
} // 就是运用的 g[i]的递推公式。这个是从根节点往下扫一边,dfs1主要是为了求除当前节点为子树外的所有节点,对当前节点的贡献值。
return Ma;
}
int main()
{
int n;
int x,y,z;
scanf("%d",&n);
for(int i = 1;i<n;i++){
scanf("%d%d%d",&x,&y,&z);
//G[u].push_back(v*1005+w); 如果爆内存了,小技巧,可以这样压缩一下,看数据范围压缩。
//G[v].push_back(u*1005+w); 或者把w[] 和 f[]合为一个数组。
node tt;
tt.y = y;
tt.z = z;
v[x].push_back(tt);
tt.y = x;
v[y].push_back(tt);
}
for(int i = 1;i<=n;i++)
scanf("%d",&w[i]);
dfs(1);
memset(book,false,(n+1)*sizeof(bool));
printf("%llu \n",dfs1(1,0,0));
return 0;
}