[bzoj1117][贪心]救火站Gas

22 篇文章 0 订阅

Description

给你一棵树,现在要建立一些消防站,有以下要求: 1. 消防站要建立在节点上,每个节点可能建立不只一个消防站。 2.
每个节点应该被一个消防站管理,这个消防站不一定建立在该节点上。 3. 每个消防站可以管理至多s个节点。 4.
消防站只能管理距离(两点间最短路径的边数)不超过k的结点。请问至少要设立多少个消防站。

Input

第一行n,s,k。接下来n-1行每行xi,yi描述一条边连接xi与yi。 1<=n<=100000 1<=s<=n 1<=k<=20
1<=xi

Output

一个数,最少的消防站个数。

Sample Input

12 3 1

1 12

3 8

7 8

8 9

2 12

10 12

9 12

4 8

5 8

8 11

6 8

Sample Output

4

题解

玄妙贪心
对于一个消防站的设立 肯定是在能管理所有子树的情况下 设的越高越好
那对于一个点 只有与他相距是K层的点是必须要染的
设状态 c [ i ] [ j ] c[i][j] c[i][j]表示i点,相距j层的还有多少可以染的点
状态 n e d [ i ] [ j ] ned[i][j] ned[i][j]表示点i,相距j层的还有多少要染的点
每一次,只有在迫不得已要染色的时候,才让子树中能对上面做贡献的点去对别的点做贡献
相当于只有在层数小于等于1的时候必须染色
否则回到父亲节点的时候,这个点已经不能对那个需要染色的点做贡献了
其它的就直接给回父亲节点,因为他们在父亲节点那里也可以被染色
总的来说
就是 在迫不得已的情况下才染色

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<queue>
#include<vector>
#include<ctime>
#define LL long long
#define mp(x,y) make_pair(x,y)
using namespace std;
inline int read()
{
	int f=1,x=0;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
	return x*f;
}
inline void write(int x)
{
	if(x<0)putchar('-'),x=-x;
	if(x>9)write(x/10);
	putchar(x%10+'0');
}
inline void print(int x){write(x);printf(" ");}
struct node{int x,y,next;}a[211000];int len,last[111000];
void ins(int x,int y){len++;a[len].x=x;a[len].y=y;a[len].next=last[x];last[x]=len;}
LL c[110000][25],ned[110000][25];
int n,S,K,ans;
void treedp(int x,int fa)
{
	ned[x][0]++;
	for(int k=last[x];k;k=a[k].next)
	{
		int y=a[k].y;
		if(y!=fa)
		{
			treedp(y,x);
			for(int i=0;i<K;i++)c[x][i]+=c[y][i+1],ned[x][i+1]+=ned[y][i];
		}
	}
	if(ned[x][K])
	{
		LL s=ceil((double)ned[x][K]/S);ans+=s;
		c[x][K]+=s*S-ned[x][K];ned[x][K]=0;
	}
	for(int i=0;i<=K;i++)
	{
		LL &tmp=c[x][i];
		if(tmp&&ned[x][i])
		{
			if(tmp>=ned[x][i])tmp-=ned[x][i],ned[x][i]=0;
			else ned[x][i]-=tmp,tmp=0;
		}
		if(tmp&&i-1>=0&&ned[x][i-1])
		{
			if(tmp>=ned[x][i-1])tmp-=ned[x][i-1],ned[x][i-1]=0;
			else ned[x][i-1]-=tmp,tmp=0;
		}
	}
}
int main()
{
	n=read();S=read();K=read();
	for(int i=1;i<n;i++)
	{
		int x=read(),y=read();
		ins(x,y);ins(y,x);
	}
	treedp(1,0);
	for(int i=0;i<=K;i++)
	{	
		LL &tmp=c[1][i];
		for(int j=0;j<=i&&tmp;j++)
			if(ned[1][j]&&tmp)
			{
				if(tmp>=ned[1][j])tmp-=ned[1][j],ned[1][j]=0;
				else ned[1][j]-=tmp,tmp=0;
			}
	}
	LL s=0;
	for(int i=0;i<=K;i++)s+=ned[1][i];
	ans+=ceil((double)s/S);
	printf("%d\n",ans);
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值