P1395

会议

题目描述

有一个村庄居住着 \(n\) 个村民,有 \(n-1\) 条路径使得这 \(n\) 个村民的家联通,每条路径的长度都为 \(1\)。现在村长希望在某个村民家中召开一场会议,村长希望所有村民到会议地点的距离之和最小,那么村长应该要把会议地点设置在哪个村民的家中,并且这个距离总和最小是多少?若有多个节点都满足条件,则选择节点编号最小的那个点。

输入格式

第一行,一个数 \(n\),表示有 \(n\) 个村民。

接下来 \(n-1\) 行,每行两个数字 \(a\)\(b\),表示村民 \(a\) 的家和村民 \(b\) 的家之间存在一条路径。

输出格式

一行输出两个数字 \(x\)\(y\)

\(x\) 表示村长将会在哪个村民家中举办会议。

\(y\) 表示距离之和的最小值。

样例 #1

样例输入 #1

4
1 2 
2 3 
3 4

样例输出 #1

2 4

提示

数据范围

对于 \(70\%\) 数据 \(n \le 10^3\)

对于 \(100\%\) 数据 \(n \le 5 \times 10^4\)

树的重心问题
树形DP
设d[i]:以i为中心点的距离和 size[i]:i以及它的子树的结点数之和
默认开始以1为root
考虑root以及它的{son}
f[son]=f[root]+(n-size[son])-size[son]
(n-size[son]):将中心从root移到son后 除了son的子树外其他点的距离+1
size[son]:son及son的子树的距离每个-1
所以就有了树形DP的转移方程
整个题首先默认以1为root dfs一遍
预处理算出size[] 顺便计算d[]
随后dp即可
点击查看代码
#include<bits/stdc++.h>
using namespace std;
int n;
struct did{
	int nxt,to;
}edge[50005*2];
int head[50005*2],cnt,d[50005],size[50005],f[50005];
inline void add(int u,int v)
{
	cnt++;
	edge[cnt].nxt=head[u];
	edge[cnt].to=v;
	head[u]=cnt;
}
void dfs(int u,int fa)
{
	size[u]=1;
	for(int i=head[u];i;i=edge[i].nxt)
	{
		int v=edge[i].to;
		if(v==fa)continue;
		d[v]=d[u]+1;
		dfs(v,u);
		size[u]+=size[v];
	}
}
void dp(int u,int fa)
{
	f[u]=f[fa]+n-2*size[u];
	for(int i=head[u];i;i=edge[i].nxt)
	{
		int v=edge[i].to;
		if(v==fa)continue;
		dp(v,u);	
	}	
} 
int main()
{
	ios::sync_with_stdio(false);
	cin>>n;
	for(int i=1;i<n;i++)
	{
		int a,b;
		cin>>a>>b;
		add(a,b);
		add(b,a);
	}
	d[1]=0;
	dfs(1,-1);
	int maxx=0,id=1;
	for(int i=1;i<=n;i++)maxx+=d[i];
	f[1]=maxx;
	for(int i=head[1];i;i=edge[i].nxt)
		dp(edge[i].to,1);
	for(int i=2;i<=n;i++)
		if(f[i]<maxx)
			maxx=f[i],id=i;
	cout<<id<<" "<<maxx<<"\n";
	return 0;
}
posted @ 2023-01-17 20:07  PKU_IMCOMING  阅读(8)  评论(0)    收藏  举报