【动态规划】【贪心】 [POI2011] DYN-Dynamite

这俩东西是怎么结合到一起的?

题目描述

给一棵树,树上有一些关键节点,要求你选 m 个点,第 i 个关键节点到这些点中每个点距离的最小值记为 disi,记这全部 dis 的最大值为 K,现在要使 K 最小,求这个 K

1n,m3×105

算法描述

让最大值最小,二分答案判断可行性。假设现在 dis 的最大值为 mid,相当于要所有的关键点到最近的选择点距离都小于等于 mid ,我们发现一棵子树内的关键点,要么已经在子树中解决,要么需要一条链从外面进来覆盖它。所以我们考虑记 fxx 子树中没有被覆盖关键点到 x 的最大距离。

但是,存在一种从子树中心选的点覆盖这个 fx 的情况,所以我们再考虑设 gxx 子树中选择的点到 x 的最小距离。

fx=maxsonfson+1

gx=minsongson+1

我们考虑几种需要修改 f,g 的情况:

  1. fx=mid ,这时如果再拖延就没有办法覆盖到这个距离为 mid 的点了,所以 fx=inf,gx=0,cnt++ ,在 x 处设关键点。

  2. fx+gxmid ,这时可以用子树中的 gx 直接覆盖 fx ,所以 fx=inf

  3. gx>mid,dx=1 ,这个点是关键点,且无法被子树中的点覆盖,所以需要记录这个关键点为 “未覆盖的点”,fx=max(fx,0)

然后二分答案时判断 cntm 即可。

时间复杂度 Θ(nlogn)

#include<bits/stdc++.h>
using namespace std;
const int N = 3e5 + 5,inf = 0x3f3f3f3f;
int f[N],g[N],n,m,cnt = 0,mid,d[N];
vector <int> G[N];
inline void dp(int x,int last)
{
	f[x] = -inf; g[x] = inf;
	for(auto to : G[x])
	{
		if(to == last) continue;
		dp(to,x);
		f[x] = max(f[x],f[to] + 1);
		g[x] = min(g[x],g[to] + 1);
	}
	if(f[x] + g[x] <= mid) f[x] = -inf;
	if(g[x] > mid && d[x] == 1) f[x] = max(f[x],0);
	if(f[x] == mid) ++cnt,f[x] = -inf,g[x] = 0;
}
inline bool check() 
{
	cnt = 0;
	dp(1,0);
	if(f[1] >= 0) cnt++;
	return cnt <= m;
}
int main()
{
	ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
	cin>>n>>m;
	for(int i = 1;i <= n;i++) cin>>d[i];
	for(int i = 1,x,y;i <= n - 1;i++)
	{
		cin>>x>>y;
		G[x].push_back(y);
		G[y].push_back(x);
	}
	int l = 0,r = n;
	while(l < r)
	{
		mid = (l + r) >> 1;
		if(check()) r = mid;
		else l = mid + 1;
	}
	cout<<l;
	return 0;
}
posted @   The_Last_Candy  阅读(7)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示