51nod 1737 配對 (貢獻、貪心、樹形dp)

題目鏈接

https://www.51nod.com/onlineJudge/questionCode.html#!problemId=1737

題意

給出一棵n個點的樹,將這n個點兩兩配對,求所有可行的方案中配對兩點間的距離的總和最大爲多少。

題解

先隨便選個點作爲根,將無根樹變成有根樹。
將邊權對應到點權(邊u-v的權值對應到v)。

距離總和大小是根據每條邊貢獻的次數來計算的,故要讓每條邊儘量貢獻多的次數。要貢獻儘量多,那麼每個點儘量找”遠”的點配對。

對於子樹u,每個想要找u子樹中的結點配對的點必須經過u結點,那麼u結點(對應的邊權)貢獻的次數最多爲min(sum[u],n-sum[u]).其中sum[u]表示u子樹的結點總數。

故dfs一次求出每個邊權對應的結點以及每個子樹的結點總數即可。
時間複雜度O(n)。

AC代碼

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e5+7;

ll val[maxn];
ll sum[maxn];
ll n;

struct edge
{
    ll to,next,w;
}e[maxn<<1];
ll head[maxn],cnt;
void init()
{
    memset(head,-1,sizeof(head));
    cnt=-1;
}
void add_edge(ll u,ll v,ll w)
{
    e[++cnt].to=v;
    e[cnt].w=w;
    e[cnt].next=head[u];
    head[u]=cnt;
}
void dfs(ll u,ll w,ll fa)
{
    sum[u]=1;
    val[u]=w;
    //cout<<u<<" "<<val[u]<<endl;
    for(ll i=head[u];i!=-1;i=e[i].next)
    {
        ll v=e[i].to;
        w=e[i].w;
        if(v==fa) continue;
        dfs(v,w,u);
        sum[u]+=sum[v];
    }
}
int main()
{
    while(~scanf("%lld",&n))
    {
        init();
        ll x,y,z;
        ll ans=0;
        memset(sum,0,sizeof(sum));
        memset(val,0,sizeof(val));
        for(ll i=1;i<n;i++)
        {
            scanf("%lld%lld%lld",&x,&y,&z);
            add_edge(x,y,z);
            add_edge(y,x,z);
        }


        dfs(1,1,-1);
        for(int i=2;i<=n;i++)
            ans+=min(sum[i],n-sum[i])*val[i];
        printf("%lld\n",ans);
    }
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章