[BZOJ1117]救火站gas

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

HINT

感谢MT大牛贡献译文.

Source

又是树上管理类的问题,我们当然考虑贪心。这个题算是消防站的设立那道题的加强版。

很显然的一种做法是对于一个点,我们要找一个深度最小的点来覆盖它。

然后看这个数据范围k<=20,我们可以想到把他当成状态放进数组里

设need[i][j]为以i为子树的j级子孙有几个未覆盖,left[i][j]为以i为子树的j级子孙有几个多出来控制的没使用

显然当need[i][j]中j=k时,我们就必须放消防站来管理了

然后我们还可以用left来抵消need

总之就是一道很巧妙的贪心题

代码:

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #define M 100010
 5 #define ll long long
 6 using namespace std;
 7 struct point{
 8     int next,to;
 9 }e[M<<1];
10 int n,m,s,k,num;
11 int head[M],fa[M];
12 ll tot,ans;
13 ll need[M][21],lef[M][21];
14 void add(int from,int to)
15 {
16     e[++num].next=head[from];
17     e[num].to=to;
18     head[from]=num;
19 }
20 void dfs(int x,int f)
21 {
22     fa[x]=f; need[x][0]=1;
23     for(int i=head[x];i;i=e[i].next)
24     {
25         int to=e[i].to;
26         if(to==fa[x]) continue;
27         dfs(to,x);
28         for(int j=1;j<=k;j++) need[x][j]+=need[to][j-1];
29         for(int j=k-1;j>=0;j--) lef[x][j]+=lef[to][j+1];
30     }
31     ll tmp=(need[x][k]+s-1)/s;
32     ans+=tmp;
33     lef[x][k]+=(ll)tmp*s;
34     for(int i=0;i<=k;i++)
35     {
36         if(lef[x][i])
37         {
38             for(int j=i;j>=0&&(j>=i-1||x==1);j--)
39             {
40                 if(lef[x][i]<=need[x][j])
41                 {
42                     need[x][j]-=lef[x][i];
43                     lef[x][i]=0;
44                     break;
45                 }
46                 lef[x][i]-=need[x][j];
47                 need[x][j]=0;
48             }
49         }
50     }
51 }
52 int main()
53 {
54     scanf("%d%d%d",&n,&s,&k);
55     for(int i=1;i<n;i++)
56     {
57         int x,y; scanf("%d%d",&x,&y);
58         add(x,y); add(y,x);
59     }
60     dfs(1,0);
61     for(int i=0;i<=k;i++) tot+=need[1][i];
62     ans+=(tot+s-1)/s;
63     printf("%lld",ans);
64     return 0;
65 }

 

转载于:https://www.cnblogs.com/Slrslr/p/9410505.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值