简要题意:
给定 个节点的一棵树,用 表示 的距离, .求最小的 和 .(其实就是求树上到一点距离之和最小的点和该距离)
.
数据加强: .
首先,我们可以统计 表示以 为根的子树大小。我们开局默认 是整棵树的根,那么树就有了层次,子树就是唯一的。
如何快速统计 呢?你会发现,如果用 表示 的父亲节点,那么 和 是可以进行转移的。这是 典型的换根 模板。
简单来说,就是,已知 ,如何 求出 呢?
实际答案不难:
- 在 的基础上转到 ,则以 为根的子树对 的贡献比对 的贡献少 ,所以需要 .(包括 本身)。
- 不包含在 为根的子树内的所有节点对 的贡献比对 的贡献多 ,所以需要 .(因为 是树根,所以 就是所有节点的个数)(这里包括 本身)
可得:
当然 我们可以在统计 的时候顺便暴力算一下。
时间复杂度: .
实际得分: .
细节:
如果在统计
的时候用 mini
和 minh
进行统计可能会出现错误。这是因为,统计
并不是按照编号大小,而是按照
的顺序,题意是 若干
相同的
输出最小的一个,所以最后我们要重新统计 minh
的答案。
#pragma GCC optimize(2)
#include<bits/stdc++.h>
using namespace std;
const int N=5e4+1;
inline int read(){char ch=getchar();int f=1; while(!isdigit(ch)) {if(ch=='-') f=-f; ch=getchar();}
int x=0;while(isdigit(ch)) x=x*10+ch-'0',ch=getchar(); return x*f;}
int a[N],n,s[N];
vector<int> G[N];
int mini,minh;
inline void dfs(int dep,int fa,int far) {
bool f=0; s[1]+=far;
for(int i=0;i<G[dep].size();i++)
if(G[dep][i]-fa) {f=1;break;}
if(!f) {a[dep]=1;return;} //叶节点
for(int i=0;i<G[dep].size();i++)
if(G[dep][i]-fa) {
dfs(G[dep][i],dep,far+1);
a[dep]+=a[G[dep][i]];
} a[dep]++; //别忘了自己
}
inline void meeting(int x,int fa) {
s[x]=s[fa]+a[1]-a[x]-a[x];
if(s[x]<mini) mini=s[x],minh=x;
for(int i=0;i<G[x].size();i++)
if(G[x][i]-fa) meeting(G[x][i],x); //统计 s 和答案
}
int main(){
n=read();
for(int i=1;i<n;i++) {
int u=read(),v=read();
G[u].push_back(v);
G[v].push_back(u);
} dfs(1,0,0); mini=s[1],minh=1;
for(int i=0;i<G[1].size();i++) meeting(G[1][i],1);
for(int i=1;i<=n;i++)
if(s[i]==mini) {minh=i; break;}
printf("%d %d\n",minh,mini); //更新答案
return 0;
}