CF1946C Tree Cutting 题解

CF1946C Tree Cutting

容易发现,如果连通块含有节点数的最小值为 x,并且使用的刀数多于或等于 k,那么 x 一定可以成为最后的结果。原因是我们可以通过减少一部分刀数,保留一个最小值的连通块,减少其他的刀数。由于 k1,所以一定可以保留一个最小值的连通块。

基于这一点,我们发现,连通块含有节点数的最小值为 x 具有单调性。因此,我们对 x 使用二分算法。

对于每一个 x,我们考虑一种刀法。显然,我们需要使用最优的刀法。我们考虑如何最小化刀数。

我们通过深度优先搜索,在回溯的过程中,每当我们发现一个子树的节点数超过 x,就直接割断它。可以发现这样做是最优的,因为一个子树的节点数超过 x,已经构成了一个点数超过 x 的连通块,没有必要再往里面增加节点。

注意需要特判根节点的连通块点数小于 x 的情况,直接返回不可能。原因是在最优化的情况下都无法解决,其余情况更不能解决。

时间复杂度 O(nlogn)

#include <bits/stdc++.h>
using namespace std;
struct edge
{
	long long v,nxt;
}e[400000];
long long t,n,k,u,v,h[400000],cnt=0,tol=0,ans=0;
void add_edge(long long u,long long v)
{
	e[++cnt].nxt=h[u];
	e[cnt].v=v;
	h[u]=cnt;
}

long long dfs(long long x,long long f,long long c)
{
	long long siz=1;
	for(int i=h[x];i;i=e[i].nxt)
	    if(e[i].v!=f)siz+=dfs(e[i].v,x,c);
	if(siz>=c&&x!=1)
	   {
	   	tol++;
	   	return 0;
	   }
	if(x==1&&siz<c&&tol==k)tol=0;
	return siz;
}

int main()
{
	scanf("%lld",&t);
	while(t--)
	   {
	   	scanf("%lld%lld",&n,&k);
	   	ans=0,cnt=0;
	   	for(int i=1;i<=n;i++)h[i]=0;
	   	for(int i=1;i<=n-1;i++)
	   	    {
	   	    scanf("%lld%lld",&u,&v);
	   	    add_edge(u,v),add_edge(v,u);
			}
		long long l=1,r=n;
		while(l<=r)
		   {
		   	long long mid=(l+r)>>1;
		   	tol=0,dfs(1,0,mid);
		   	if(tol>=k)ans=mid,l=mid+1;
		   	else r=mid-1;
		   }
		printf("%lld\n",ans);
	   }
	return 0;
}
posted @   w9095  阅读(1)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· NetPad:一个.NET开源、跨平台的C#编辑器
· PowerShell开发游戏 · 打蜜蜂
· 凌晨三点救火实录:Java内存泄漏的七个神坑,你至少踩过三个!
点击右上角即可分享
微信分享提示