1. 程式人生 > >[POI2009]救火站Gas

[POI2009]救火站Gas

轉移 個數 為我 技術 cstring 一個 ram printf 管理距離

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
技術分享圖片

這題容易想到一個貪心策略,消防站必定先控制距離為k的點,然後距離遞減,但是這個東西我們不好維護,所以我們就可以維護兩個數組,用這兩個數組來互相抵消維護。
轉移的時候有一句話需要註意:

for (int i=k;i;i--)     more[x][i-1]+=more[son][i];

為什麽是i來更新i-1,因為我控制的點有兩個方向,一邊向兒子,一邊向父親,向父親方向的點顯然要這麽更新;由於兒子方向的點被兩個數組互相抵消了兩個距離的節點,所以這樣更新是沒有問題的

/*program from Wolfycz*/
#include<cmath>
#include<cstdio> #include<cstring> #include<iostream> #include<algorithm> #define inf 0x7f7f7f7f using namespace std; typedef long long ll; typedef unsigned int ui; typedef unsigned long long ull; inline int read(){ int x=0,f=1;char ch=getchar(); for (;ch<‘0‘||ch>‘9‘
;ch=getchar()) if (ch==‘-‘) f=-1; for (;ch>=‘0‘&&ch<=‘9‘;ch=getchar()) x=(x<<1)+(x<<3)+ch-‘0‘; return x*f; } inline void print(int x){ if (x>=10) print(x/10); putchar(x%10+‘0‘); } const int N=1e5,M=20; int pre[(N<<1)+10],now[N+10],child[(N<<1)+10],tot; ll more[N+10][M+10],need[N+10][M+10]; int n,m,k,Ans; void join(int x,int y){pre[++tot]=now[x],now[x]=tot,child[tot]=y;} void dfs(int x,int fa){ need[x][0]=1; for (int p=now[x],son=child[p];p;p=pre[p],son=child[p]){ if (son==fa) continue; dfs(son,x); for (int i=0;i<k;i++) need[x][i+1]+=need[son][i]; for (int i=k;i;i--) more[x][i-1]+=more[son][i]; } int T=(need[x][k]+m-1)/m; Ans+=T; more[x][k]+=1ll*T*m; for (int i=0;i<=k;i++){ if (more[x][i]){ for (int j=i;~i&&(j>=i-1||x==1);j--){//抵消掉兩個距離的節點 if (more[x][i]<=need[x][j]){ need[x][j]-=more[x][i]; more[x][i]=0; break; } more[x][i]-=need[x][j]; need[x][j]=0; } } } } int main(){ n=read(),m=read(),k=read(); for (int i=1;i<n;i++){ int x=read(),y=read(); join(x,y),join(y,x); } dfs(1,0); int tot=0; for (int i=0;i<=k;i++) tot+=need[1][i]; Ans+=(tot+m-1)/m; printf("%d\n",Ans); return 0; }

[POI2009]救火站Gas