1. 程式人生 > 實用技巧 >聯賽模擬測試25 C. Repulsed 貪心+樹形DP

聯賽模擬測試25 C. Repulsed 貪心+樹形DP

題目描述


分析

考慮自底向上貪心
\(f[x][k]\) 表示 \(x\) 下面距離為 \(k\) 的需要滅火器的房間數,\(g[x][k]\)
表示 \(x\) 下面距離為 \(k\) 的多餘滅火器數
每個滅火器和房間的匹配在 \(lca\) 處處理
每次假設子樹裡已經最優了,那麼 \(f[x][k]\) 一定要用 \(g[x][0]\)
填滿
然後距離為 \(k\) 的一定會在 \(x\) 處匹配掉,否則到上面不會更
優(可以交叉互換)
在不存在距離為 \(k\) 的前提下,\(k-1\) 一定會在 \(x\) 處匹配掉否則
可以交叉互換
根處 \(g\)\(f\) 的匹配再做一個簡單的貪心即可

程式碼

#include<cstdio>
#include<cmath>
#include<algorithm>
#include<cstring>
#define rg register
inline int read(){
	rg int x=0,fh=1;
	rg char ch=getchar();
	while(ch<'0' || ch>'9'){
		if(ch=='-') fh=-1;
		ch=getchar();
	}
	while(ch>='0' && ch<='9'){
		x=(x<<1)+(x<<3)+(ch^48);
		ch=getchar();
	}
	return x*fh;
}
const int maxn=1e5+5,maxk=22;
int h[maxn],tot=1,n,s,k;
struct asd{
	int to,nxt;
}b[maxn<<1];
void ad(int aa,int bb){
	b[tot].to=bb;
	b[tot].nxt=h[aa];
	h[aa]=tot++;
}
int f[maxn][maxk],g[maxn][maxk],siz[maxn],ans;
void dfs(int now,int fa){
	f[now][0]++;
	for(rg int i=h[now];i!=-1;i=b[i].nxt){
		rg int u=b[i].to;
		if(u==fa) continue;
		dfs(u,now);
		for(rg int j=0;j<k;j++){
			f[now][j+1]+=f[u][j];
			g[now][j+1]+=g[u][j];
			if(g[now][j+1]>n) g[now][j+1]=n;
		}
	}
	while(f[now][k] && g[now][0]<f[now][k]){
		g[now][0]+=s;
		ans++;
	}
	for(rg int i=0;i<=k;i++){
		rg int cs=std::min(f[now][k-i],g[now][i]);
		f[now][k-i]-=cs;
		g[now][i]-=cs;
	}
	for(rg int i=0;i<k;i++){
		rg int cs=std::min(f[now][k-i-1],g[now][i]);
		f[now][k-i-1]-=cs;
		g[now][i]-=cs;
	}
}
int main(){			
	memset(h,-1,sizeof(h));
	n=read(),s=read(),k=read();
	rg int aa,bb;
	for(rg int i=1;i<n;i++){
		aa=read(),bb=read();
		ad(aa,bb);
		ad(bb,aa);
	}
	dfs(1,0);
	for(rg int i=0;i<=k;i++){
		for(rg int j=k-i;j>=0;j--){
			rg int cs=std::min(f[1][j],g[1][i]);
			f[1][j]-=cs;
			g[1][i]-=cs;
		}
	}
	rg int nans=0;
	for(rg int i=0;i<=k;i++){
		nans+=f[1][i];
	}
	if(nans%s==0) ans+=nans/s;
	else ans+=nans/s+1;
	printf("%d\n",ans);
	return 0;	
}