P1395 会议 题解

博客园同步

原题链接

简要题意:

给定 n n 个节点的一棵树,用 dis ( x , y ) \text{dis}(x,y) 表示 x , y x,y 的距离, s x = y = 1 n dis ( x , y ) s_x = \sum_{y=1}^n \text{dis}(x,y) .求最小的 x x s x s_x .(其实就是求树上到一点距离之和最小的点和该距离)

n 5 × 1 0 4 n \leq 5 \times 10^4 .

数据加强: n 1 0 7 n \leq 10^7 .

首先,我们可以统计 a x a_x 表示以 x x 为根的子树大小。我们开局默认 1 1 是整棵树的根,那么树就有了层次,子树就是唯一的。

如何快速统计 s x s_x 呢?你会发现,如果用 f a x {fa}_x 表示 x x 的父亲节点,那么 s x s_x s f a x s_{{fa}_x} 是可以进行转移的。这是 典型的换根 dp \text{dp} 模板。

简单来说,就是,已知 s f a x s_{{fa}_x} ,如何 O ( 1 ) \mathcal{O}(1) 求出 s x s_x 呢?

实际答案不难:

  • s f a x s_{{fa}_x} 的基础上转到 s x s_x ,则以 x x 为根的子树对 x x 的贡献比对 f a x {fa}_x 的贡献少 1 1 ,所以需要 a x -a_x .(包括 x x 本身)。
  • 不包含在 x x 为根的子树内的所有节点对 x x 的贡献比对 f a x {fa}_x 的贡献多 1 1 ,所以需要 + a 1 a x +a_1 - a_x .(因为 1 1 是树根,所以 a 1 a_1 就是所有节点的个数)(这里包括 f a x {fa}_x 本身)

可得:

s x = s f a x a x + a 1 a x s_x = s_{{fa}_x} - a_x + a_1 - a_x

当然 s 1 s_1 我们可以在统计 a a 的时候顺便暴力算一下。

时间复杂度: O ( n ) \mathcal{O}(n) .

实际得分: 100 p t s 100pts .

细节:

如果在统计 s s 的时候用 miniminh 进行统计可能会出现错误。这是因为,统计 s s 并不是按照编号大小,而是按照 dfs \text{dfs} 的顺序,题意是 若干 s x s_x 相同的 x x 输出最小的一个,所以最后我们要重新统计 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;
}

猜你喜欢

转载自blog.csdn.net/bifanwen/article/details/107443511