1. 程式人生 > >洛谷P2986

洛谷P2986

簡化版題意:給一個邊點都帶權的樹,求這棵樹的重心到所有點的邊權點權和。

 

一般題意:

有個 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;
}