洛谷P2986

版权声明:本文为博主原创文章,未经博主允许不得转载。 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;
}

猜你喜欢

转载自blog.csdn.net/qq_40924940/article/details/84986273