JZOJ5400 Repulsed(树上dp+贪心)

博客介绍了如何利用树上动态规划(DP)和贪心策略解决JZOJ5400题目。讨论了如何确定节点的最佳灭火位置,并详细解释了如何计算不同距离的待灭点数和灭火器覆盖范围。通过寻找距离为k和k-1的组合,确保灭火器能覆盖到最大范围,同时考虑根节点的特殊情况,以实现最优解。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

对于一个点,它被它的k级父亲覆盖肯定是最优的。

f[i][j] :i这个点的子树中和它距离为j的待灭点数。

g[i][j]:i这个点的子树中和它距离为j的灭火器能灭点数。

f[i][k]:一定是在i这里灭是最优的,因为相距k.

对于与i相距z的的灭火器(所在点)d2和与i相距w的待灭点d1,如果z + w = k或z + w = k-1(如果再往上走d1,d2就相距k+1就灭不到了)。如果灭火器还能灭,就灭。//每次找距离为k,k-1的,贪心思想,其实i是d1和d2的lca)

对于根来说我们能灭就灭。也就是d1 + d2 < = k 就灭。//距离根小与k的没有k级父亲啊,不能被他的k级父亲灭啊,贪心不了。

再统计有多少灭不了的,加灭火器。

 

#include<bits/stdc++.h>
using namespace std;
int tp, tov[200005], nex[200005], h[100005], n, s, k, f[100005][22], g[100005][22], ans;
void read(int &x)
{
	int f = 0; char  c = getchar(); x = 0;
	while(c < '0' || c > '9')
	{
		if(c == '-') f = 1; c = getchar();
	}
	while(c >= '0' && c <= '9')
	{
		x = x * 10 + c  - '0'; c = getchar();
	}
	if(f) x = -x;
}
void add(int x,int y)
{
	tp++; tov[tp] = y; nex[tp] = h[x]; h[x] = tp;
}
int get(int x)
{
	if(x == 0) return 0;
	return (x-1) / s + 1;
}
void dfs(int x,int fa)
{
	for(int i = h[x]; i; i = nex[i])
	{
		int v = tov[i]; if(v == fa) continue;
		dfs(v,x);
		for(int j = 1; j <= k; j++)
		{
		f[x][j] = f[x][j] +f[v][j-1]; 		g[x][j] = min(g[x][j]+g[v][j-1],n); 	
		}
	}
	f[x][0]++;
	if(f[x][k])
	{
		int ha = get(f[x][k]);
	     g[x][0] =min(n, ha * s);
	     ans += ha;
	}
	for(int i = 0; i <= k; i++)
	{
		int j = k - i;
		int ha = min(f[x][i],g[x][j]);
		f[x][i] -= ha; g[x][j] -= ha;
	}
	for(int i = 0; i <= k -1; i++)
	{
		int j = k - 1 - i;
		int ha = min(f[x][i],g[x][j]);
		f[x][i] -= ha; g[x][j] -= ha;
	}
}
int main()
{
	  freopen("repulsed.in","r",stdin);
	freopen("repulsed.out","w",stdout);
	read(n); read(s); read(k);
	for(int i = 1; i < n; i++)
	{
		int x,y;read(x); read(y); add(x,y); add(y,x);
	}
	dfs(1,1);
	for(int i = 0; i <= k; i++)
	 for(int j = 0; j <= k ;j ++)
	 {
	 	if(i + j <= k)
	 	{
	 		int ha = min(f[1][i],g[1][j]);
		   f[1][i] -= ha; g[1][j] -= ha;	
	 	}
	 }
	 int tmp = 0;
	 for(int i = 0 ; i<= k; i++)
	 tmp += f[1][i];
	 ans += get(tmp);
	 cout << ans;
	 return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值