#6042. 「雅礼集训 2017 Day7」跳蚤王国的宰相

#6042. 「雅礼集训 2017 Day7」跳蚤王国的宰相

内存限制:1024 MiB 时间限制:2000 ms 标准输入输出
题目类型:传统 评测方式:文本比较
上传者: 匿名
题目描述

跳蚤王国爆发了一场动乱,国王在镇压动乱的同时,需要在跳蚤国地方钦定一个人来做宰相。

由于当时形势的复杂性,很多跳蚤都并不想去做一个傀儡宰相,带着宰相的帽子,最后还冒着被打倒并杀头的危险,然而有一只跳蚤却想得与众不同最时尚。

本来他打算去教书,他已经发表了自己在学术方面的见解,获得了很多跳蚤们的赞同,但是这时听说跳蚤国要钦定宰相,他毅然打断了想去教书的想法,他觉得只要为了国家利益,自己的生死都可以不管,哪里能因为工作能给自己带来灾祸或者福分就去避开或者接近这份工作呢?所以他决定站出来接了这份工作。

然而当时国王的钦定方式很奇怪,跳蚤王国可以看作一棵树,国王认为宰相必须更好的为跳蚤服务,所以他会选择一个到所有节点距离和最小的节点,并在这个节点中钦定,如果有多个节点满足距离和最小则任选一个。

然而跳蚤国的动乱实在是太厉害了,以至于树的形态可能也会发生改变,也就是说,树上可能会有若干条边消失,如果这个情况出现的话一定会有同样数目的边出现,以保证整个结构仍然是一棵树

现在这个跳蚤想知道每个节点中的跳蚤如果要被钦定,至少需要多少条边消失(当然也会有同样数目的边出现)。作为这只跳蚤的一名真正的粉丝,你能帮他解决这个问题吗?

输入格式

第一行一个正整数 n nn 表示树中点的个数。
接下来 n−1 n - 1n1 行,每行两个正整数 u uuv vv,表示点 u uu 与点 v vv 之间有一条树边。

输出格式

输出 n nn 行,第 i ii 行一个数,表示第 i ii 个节点如果要被钦定至少需要多少条边消失。

样例
样例输入 1
10
1 2
1 3
1 4
1 5
1 6
1 7
1 8
1 9
1 10
样例输出 1
0
4
4
4
4
4
4
4
4
4
4
数据范围与提示

对于 10% 10\%10% 的数据,n≤10 n \leq 10n10
对于 40% 40\%40% 的数据,n≤2000 n \leq 2000n2000
对于 70% 70\%70% 的数据,n≤100000 n \leq 100000n100000
对于 100% 100\%100% 的数据,10≤n≤1000000 10 \leq n \leq 100000010n1000000

额,我该说什么呢?

首先很容易发现这个点被选中时没有任意一个儿子的siz>n/2

除的话涉及取整,可能表意错误,我们用乘:siz*2>n

然后,你就发现重心满足性质。

然后,你就发现每个点最多只有一个儿子会破坏性质,

然后,你就发现那个儿子一定是在那个点往重心的位置,

设那个点为A,重心为B

你会发现,A到B之间的边都不需要割,只需要割B周围的边(因为B的儿子的siz都满足条件),
然后,就在B旁边选最少的边使其满足条件。贪心即可。

然后,你会发现,这些方案大多数是差不多的,一般只会相差一个或0个。

然后,用你的脑子优化程序就AC了!

但是为什么会想到重心呢?

因为经验。

为什么在这个科学主义的社会仍有这样经验主义的封建残余呢?

因为这是人类的智慧。

ACcode:

#include<cstdio>
#include<cstring>
#include<cctype>
#include<algorithm>
#include<vector>
#define maxn 1000005
using namespace std;

int n,siz[maxn],esiz[maxn],ans[maxn];
vector<int>g[maxn];
int sum=0,cnt=0;

int rt,Min;

void dfs(int now,int ff,int tsz){
	int Max=0;
	siz[now]=1;
	for(int i=0;i<esiz[now];i++)
		if(g[now][i]!=ff){
			dfs(g[now][i],now,tsz);
			siz[now]+=siz[g[now][i]];
			Max=max(Max,siz[g[now][i]]);
		}
	
	Max=max(Max,tsz-siz[now]);
	if(Max<Min){
		rt=now;
		Min=Max;
	}
}

int Gert(int now,int tsz){
	Min=0x3f3f3f3f;
	dfs(now,-1,tsz);
	return rt;
}

bool cmp(const int &a,const int &b){
	return siz[a]>siz[b];
}

void ser(int now,int ff,int usd){
	ans[now]=cnt+( (n-usd-siz[now])*2 > n);
	for(int i=0;i<esiz[now];i++)
		if(g[now][i]!=ff)
			ser(g[now][i],now,usd);
}

int main(){
	int u,v;
	scanf("%d",&n);
	for(int i=1;i<n;i++){
		scanf("%d %d",&u,&v);
		g[u].push_back(v);
		g[v].push_back(u);
		esiz[u]++,esiz[v]++;
	}
	
	int A=Gert(1,n);
	dfs(A,-1,0);
	sort(g[A].begin(),g[A].end(),cmp);
	
	for(int i=0;i<esiz[A];i++){
		sum=(sum+siz[g[A][i]]);
		if((sum<<1)>=n) break;
		cnt++;//本来答案应该是cnt或cnt-1的,故意少加一,在解决函数中加一
	}
	
	for(int i=0;i<esiz[A];i++)
		ser(g[A][i],A,sum-max(siz[g[A][i]],siz[g[A][cnt]]));//当前子树未被割,于是max会选siz[g[A][cnt]],被割了,就会选siz[g[A][i]]
	
	for(int i=1;i<=n;i++)
		printf("%d\n",ans[i]);
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值