聯賽模擬測試25 C. Repulsed 貪心+樹形DP
阿新 • • 發佈:2020-10-29
題目描述
分析
考慮自底向上貪心
\(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; }