版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_40924940/article/details/84986273
简化版题意:给一个边点都带权的树,求这棵树的重心到所有点的边权点权和。
一般题意:
有个 n 个牧场,每个牧场有 A_i个奶牛,不同牧场之间有距离,求以某一个点为一次聚会的举办地,使得其他所有点到这个点花费最少(花费等同于 一个节点到这个节点的距离 * 一个节点内的奶牛数目)
首先我们考虑一下 树的重心这一概念,现在有一个直接结论,边为无向边的树的边权对树中心的位置不会造成影响。
听上去可能感觉有点不可思议,但是事实就是边权不会影响树中心位置,因为边是无向边,我们可以想几个极端例子
假设 A-B之间边权 为 INF 虽然看似其他点到 A 的距离变远了,但是 A 到其他点的距离也变远了, 所以树的重心和边并没有太大关系,有了这个结论我们就很好去搞了。。。下边是某博客上写的定义
- 树的重心:也叫树的质心。找到一个点,其所有的子树中最大的子树节点数最少,那么这个点就是这棵树的重心,删去重心后,生成的多棵树尽可能平衡。
暂时抛去边不看,我们可以设置三个东西,一个存储节点里的奶牛数,另一存储本节点子树上的奶牛数总和,最后一个自然就是存储我们的dp,来记录一下该点所有的子树中最大的子树节点数,最后根据 dp 来决定以谁作为重心,跑一边简单的最短路。。一路上记录权值和就好了。。(因为这里我们可以采用分层的思路,所以最短路在树里还是很好跑的)
以下是 AC 代码
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
#define ll long long int
const int maxn = 1e6+5;
struct node
{
ll nxt;
ll to;
ll val;
}ed[maxn];
ll head[maxn],tot,sum;
void add(ll u,ll v,ll w)
{
ed[++tot].to = v;
ed[tot].nxt = head[u];
ed[tot].val = w;
head[u] = tot;
}
ll dp[maxn];
ll num[maxn];
ll ans[maxn];
void dfs(ll s,ll p)
{
num[s] = ans[s];
for(ll i=head[s];~i;i=ed[i].nxt)
{
ll to = ed[i].to;
if(to != p)
{
dfs(to, s);
num[s] += num[to];//点s为根的子树的结点个数
dp[s] = max(dp[s], num[to]);//这个记录最大子树节点数
}
}
dp[s] = max(dp[s], sum - num[s]);//最后比较一下父节点
}
void spfa(ll s,ll p)
{
for(ll i=head[s];~i;i=ed[i].nxt)
{
ll to = ed[i].to;
if(to != p)
{
num[to] = num[s] + ed[i].val;
spfa(to, s);
}
}
}
ll n;
int main()
{
scanf("%d",&n);
sum = 0;
tot = 0;
memset(head,-1,sizeof head);
for(ll i=1;i<=n;i++)
{
scanf("%lld",&ans[i]);
sum += ans[i];
}
for(ll i=1;i<n;i++)
{
ll x,y,z;
scanf("%lld%lld%lld",&x,&y,&z);
add(x,y,z);
add(y,x,z);
}
dfs(1, 1);
ll id = 1;
for(ll i=2;i<=n;i++)
{
if(dp[i] < dp[id])
id = i;
}
num[id] = 0;
spfa(id, id);
ll ant = 0;
for(ll i=1;i<=n;i++)
{
ant += num[i] * ans[i];
}
printf("%lld\n",ant);
return 0;
}