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;
}
此生无悔入OI 来生AK IOI

浙公网安备 33010602011771号