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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值