CF1946C Tree Cutting 题解
容易发现,如果连通块含有节点数的最小值为 ,并且使用的刀数多于或等于 ,那么 一定可以成为最后的结果。原因是我们可以通过减少一部分刀数,保留一个最小值的连通块,减少其他的刀数。由于 ,所以一定可以保留一个最小值的连通块。
基于这一点,我们发现,连通块含有节点数的最小值为 具有单调性。因此,我们对 使用二分算法。
对于每一个 ,我们考虑一种刀法。显然,我们需要使用最优的刀法。我们考虑如何最小化刀数。
我们通过深度优先搜索,在回溯的过程中,每当我们发现一个子树的节点数超过 ,就直接割断它。可以发现这样做是最优的,因为一个子树的节点数超过 ,已经构成了一个点数超过 的连通块,没有必要再往里面增加节点。
注意需要特判根节点的连通块点数小于 的情况,直接返回不可能。原因是在最优化的情况下都无法解决,其余情况更不能解决。
时间复杂度 。
#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;
}
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· NetPad:一个.NET开源、跨平台的C#编辑器
· PowerShell开发游戏 · 打蜜蜂
· 凌晨三点救火实录:Java内存泄漏的七个神坑,你至少踩过三个!