题目描述
分析
考虑自底向上贪心
(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
",ans);
return 0;
}