题意:树上选一个点使所有点到它的距离之和最小,输出其编号和最小距离之和,如有多个,输出编号最小的一个
题解:设dp[i]位选i点时所有点到它的距离之和,考虑父子关系可得转移方程dp[now]=dp[father]+(n-size[now])-size[now],问题就在于从哪里开始转移?可以求得dp[1],然后向下转移,至于dp[1]怎么求:dp[1]=Σsize[i](i≠1)。
一共dfs两遍,第一遍求size和Σsize,第二遍递推dp值。
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int N=5e4+4;
int n;
int head[N],etot=0;
struct EDGE {
int v,nxt;
}e[N<<1];
int dp[N],siz[N],ssiz[N];
inline int read() {
int x=0;char c=getchar();
while (c<'0'||c>'9') c=getchar();
while (c>='0'&&c<='9') x=x*10+c-'0',c=getchar();
return x;
}
inline void adde(int u,int v) {
e[++etot].nxt=head[u],e[etot].v=v,head[u]=etot;
}
void dfs1(int p,int fa) {
ssiz[p]=0,siz[p]=1;
for (int i=head[p];~i;i=e[i].nxt) {
int v=e[i].v;
if (v==fa) continue;
dfs1(v,p);
siz[p]+=siz[v];
ssiz[p]+=ssiz[v];
}
ssiz[p]+=siz[p];
}
void dfs2(int p,int fa) {
for (int i=head[p];~i;i=e[i].nxt) {
int v=e[i].v;
if (v==fa) continue;
dp[v]=dp[p]+n-(siz[v]<<1);
dfs2(v,p);
}
}
int main() {
memset(head,-1,sizeof(head));
n=read();
for (register int i=1;i<n;++i) {
int u=read(),v=read();
adde(u,v),adde(v,u);
}
dfs1(1,0);
for (int i=head[1];~i;i=e[i].nxt)
dp[1]+=ssiz[e[i].v];
dfs2(1,0);
int mn=0x3f3f3f3f;
for (register int i=1;i<=n;++i) mn=min(mn,dp[i]);
for (register int i=1;i<=n;++i)
if (dp[i]==mn) {
printf("%d %d\n",i,mn);
break;
}
return 0;
}