BZOJ1117: [POI2009]救火站Gas

題目大意:給你一棵樹,要求你在一些節點建立消防站(一個節點可以建多個),每個消防站可以管理與它距離不超過K的最多S個節點,要求每個節點都至少被一個消防站管理,問最少需要安排幾個消防站


一看到這種代價都是1的樹上管理問題就能想到貪心...

也就是說我們能想到一種貪心策略:以1爲根建樹,從葉子開始,能不放消防站就不放,直到必須放了(有沒被覆蓋節點與當前節點距離爲K)的時候再放消防站

這樣我們可以在每個節點維護兩個大小爲K的數組

第一個數組記錄這個節點的子樹中還沒被覆蓋的點中距離爲i的有多少個

第二個數組記錄這個節點的子樹中控制範圍爲i的消防站還能再控制幾個節點


這樣一來我們可以非常容易的轉移,但是我們面臨一個問題,如何讓這兩個數組相互抵消?

我們知道當我們用一個剩餘覆蓋範圍很廣的消防站去覆蓋一個離的很近的消防站顯然是浪費

所以我們抵消的原則是:再過一步就抵消不了了的時候,我們纔去抵消

也就是說,若這個消防站控制範圍爲i,我們只去抵消距離爲i和i-1的節點


要開long long 真是坑啊....不過不知道爲啥要開...

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 200010
using namespace std;
int to[N],nxt[N],pre[N],cnt;
void ae(int ff,int tt)
{
	cnt++;
	to[cnt]=tt;
	nxt[cnt]=pre[ff];
	pre[ff]=cnt;
}
int fa[N];
int n,S,K;
long long que[N][21],duo[N][21];
int ans;
void build(int x)
{
	int i,j,k;
	que[x][0]=1;
	for(i=pre[x];i;i=nxt[i])
	{
		j=to[i];
		if(j==fa[x]) continue;
		fa[j]=x;
		build(j);
		for(k=0;k<K;k++)
		que[x][k+1]+=que[j][k];
		for(k=K;k>0;k--)
		duo[x][k-1]+=duo[j][k];
	}
	k=(que[x][K]+S-1)/S;
	ans+=k;
	duo[x][K]+=k*S;
	for(j=0;j<=K;j++)
	if(duo[x][j])
	{
		for(k=j;k>=0&&(k>=j-1||x==1);k--)
		{
			if(duo[x][j]<=que[x][k])
			{
				que[x][k]-=duo[x][j];
				duo[x][j]=0;
				break;
			}
			duo[x][j]-=que[x][k];
			que[x][k]=0;
		}
	}
}
int main()
{
	scanf("%d%d%d",&n,&S,&K);
	int i,j,x,y;
	for(i=1;i<n;i++)
	{
		scanf("%d%d",&x,&y);
		ae(x,y);ae(y,x);
	}
	build(1);
	int tot=0;
	for(i=0;i<=K;i++)
	tot+=que[1][i];
	ans+=(tot+S-1)/S;
	printf("%d",ans);
}


發佈了170 篇原創文章 · 獲贊 42 · 訪問量 16萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章