0
点赞
收藏
分享

微信扫一扫

51nod 1737 配对 (树的重心)


1737 配对

  1. 1 秒
  2.  
  3. 131,072 KB
  4.  
  5. 40 分
  6.  
  7. ​​4 级题​​

给出一棵n个点的树,将这n个点两两配对,求所有可行的方案中配对两点间的距离的总和最大为多少。

 收起

输入

一个数n(1<=n<=100,000,n保证为偶数)
接下来n-1行每行三个数x,y,z表示有一条长度为z的边连接x和y(0<=z<=1,000,000,000)

输出

一个数表示答案

输入样例

6
1 2 1
1 3 1
1 4 1
3 5 1
4 6 1

输出样例

7
//配对方案为(1,2)(3,4)(5,6)

​​

51nod 1737 配对 (树的重心)_子树

​​

出题人﹡ LH

 

分析:

画图可以得知对于每一条边,它对答案的贡献一定不超过它两端子树的大小的较小值,并且使答案最大的每条路径都应该是经过重心的,因此题目可以转化为求所有点到重心的权值和。(直接按发现的规律求也可以)。

#include <stdio.h>
#include <string.h>
#include <iostream>
#include <algorithm>
#include <stack>
#include <vector>
#include <queue>
#include <set>
#include <map>
#include <string>
#include <math.h>
#include <stdlib.h>
#include <time.h>
using namespace std;
typedef long long ll;
const int N=100010;
struct node
{
int v;///终端点
int next;///下一条同样起点的边号
ll w;///权值
} edge[N*2]; ///无向边,2倍
int head[N];///head[u]=i表示以u为起点的所有边中的第一条边是 i号边
int tot; ///总边数
void add(int u,int v,ll w)
{
edge[tot].v=v;
edge[tot].w=w;
edge[tot].next=head[u];
head[u]=tot++;
}
int n;
int dp[N],num[N];
///num[i] 保存自己所有除祖先以外所有子树的节点总和
///dp[i]保存所有子树中最大的子树的节点数
void dfs(int u,int fa) ///遍历图求树的重心
{
num[u]=1;
for(int i=head[u]; i!=-1; i=edge[i].next)
{
int v= edge[i].v;
if(fa==v) continue;
dfs(v,u);
dp[u]=max(dp[u],num[v]);
num[u]+=num[v];
}
dp[u]=max(dp[u],n-num[u]);
}
ll ans=0;
void get_sum(int u,int fa,ll dis)///求所有点到树的重心的距离
{
ans+=dis;
for(int i=head[u];i!=-1;i=edge[i].next)
{
int v= edge[i].v;
if(fa==v) continue;
get_sum(v,u,dis+edge[i].w);
}
}
int main()
{
while(scanf("%d",&n)!=-1)
{
memset(head,-1,sizeof(head));
memset(dp,-1,sizeof(dp));
tot=0;
ans=0;

for(int i=1; i<n; i++)
{
int u,v ;
ll w;
scanf("%d%d%lld",&u,&v,&w);
add(u,v,w);
add(v,u,w);
}
dfs(1,-1);
int root_point;
int sizee = 1e9;
for(int i = 1; i <= n; i ++)
{
if(dp[i] < sizee)
{
root_point = i;
sizee = dp[i];
}
}
//get_sum(root_point,-1,0);
//printf("%lld\n",ans);
printf("%d %d\n",root_point, sizee);//输出字典序最小的树的重心和最大子树的节点数量


}
return 0;
}

 

举报

相关推荐

0 条评论