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
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 }