POJ1655 Balancing Act 题解

1 篇文章 0 订阅

博客园同步

原题链接

简要题意:

给定一棵树,求它的重心,以及 以它为根的最大子树的大小。

重心的定义: i i i 为根的最大子树最小的 i i i 编号。

首先扫清一个误区:

初阶的 Oier \texttt{Oier} Oier 可能觉得,那重心就是叶子节点了?

不是这样的。如果把叶子结点拿起来作为根,那么 除叶子结点外的其它所有节点都是一个子树,所以 一般来说 叶子结点并不是重心。(也不排除深度 = 2 =2 =2 的情况)

那么,其实以 i i i 为根的最大子树分为两部分:

  1. 当前以 i i i 为根的子树个数(包括自己),记作 d i d_i di.

  2. 不包含在 i i i 为根的子树中的,即 s u m − d i sum - d_i sumdi.( s u m sum sum 表示一共有多少个点)

显然,我么可以用一重 dfs \text{dfs} dfs 求出 d i d_i di,然后打擂即可得出。

细节: s u m ≠ n sum \not = n sum=n,只是节点 ≤ n \leq n n,不一定会出现。

时间复杂度: O ( n ) O(n) O(n).

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

#pragma GCC optimize(2)
#include<cstdio>
#include<vector>
#include<string.h> //memset 在这个库里
#include<iostream>
#include<algorithm> //POJ 不支持万能头,只能手写
using namespace std;

const int N=2e5+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 n,mini,minh,T;
int d[N];
vector<int> G[N];

inline int dfs(int dep,int fa) {
	d[dep]=0; int t=0;
	for(int i=0;i<G[dep].size();i++) {
		int v=G[dep][i]; if(v==fa) continue;
		dfs(v,dep); d[dep]+=d[v]+1; //d[i] 是子树大小(不含自己),统计的时候要算儿子自己,所以 +1
		t=max(t,d[v]+1); //得到最大子树
	} t=max(t,n-d[dep]-1); //和另一边的子树比较,得到最大子树
	if(t<mini || (t==mini && dep<minh)) mini=t,minh=dep; //更新答案
}

int main(){
	T=read(); while(T--) {
		n=read();
		for(int i=1;i<n;i++) {
			int u=read(),v=read();
			G[u].push_back(v);
			G[v].push_back(u); //建图
		} minh=1e9; mini=1e9; dfs(1,0); //搜索
		printf("%d %d\n",minh,mini); //答案
		memset(d,0,sizeof(d));
		for(int i=1;i<=n;i++) G[i].erase(G[i].begin(),G[i].end()); //多组数据初始化
	}
	return 0;
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值