题目链接
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;
}