【CF 510 F】Leaf Set

题目链接

题意

给定一棵树,要求把叶子节点分成最少的集合,使得集合内点对两两距离不超过K

Sol

套路: 要求点对间两两距离不超过K,等价于在每条边上新建一个点,然后找到一个点使得它到所有集合内的点的距离不超过K

这样转化完就变成了选取最少的点覆盖所有的叶子,就是个很裸的贪心了 (然而打比赛的时候并没有看出来TAT)

正确性:
对于本来的一条长度为 L 的路径,把边拆成点后必然路径长度变为 2L , 那么只要点集内所有的点都能到达你选的点,他们到达彼此的距离就小于等于 2K , 在原图中就会 小于等于 K , 这样就证明了正确性

代码:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<queue>
#include<set>
using namespace std;
const int N=2e6+10;
inline int read()
{
	char ch=getchar();int t=1;int x=0;
	for(;ch>'9'||ch<'0';ch=getchar()) if(ch=='-') t=-1;
	for(;ch>='0'&&ch<='9';ch=getchar()) x=(x<<1)+(x<<3)+(ch-48);
	return x*t;
}
struct edge{
	int to,next;
}a[N<<1];
int head[N];int cnt=0;
int K;
int n;
const int INF=1e9;
inline void add(int x,int y){a[++cnt]=(edge){y,head[x]};head[x]=cnt;}
int near[N],away[N];
int du[N];
int ans=0;
void dfs(int u,int fa)
{
	near[u]=INF;away[u]=-INF;
	for(register int v,i=head[u];i;i=a[i].next)
	{
		v=a[i].to;if(v==fa) continue;
		dfs(v,u);near[u]=min(near[v]+1,near[u]);
		if(near[v]+away[v]>K) away[u]=max(away[u],away[v]+1);
	}
	if(fa) {if(away[u]>=K) ++ans,away[u]=-INF,near[u]=0; else if(du[u]==1) away[u]=0;}
	else {
		if(du[u]==1) away[u]=max(away[u],0);
		if(near[u]+away[u]>K) ++ans;
	}
	return;
}
int main()
{
	n=read();K=read();register int u,v;
	for(register int i=n-1;i;--i){
		u=read();v=read();++n;
		add(u,n);add(n,u);add(v,n);add(n,v);++du[u];++du[v];
	}
	dfs(1,0);
	printf("%d\n",ans);
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值