[BZOJ4027][HEOI2015]兔子与樱花
description
一棵\(n\)个点的树,每个点上有一个权值\(c_i\)。你可以删掉任意一个除根之外的节点,删除后这个点的权值和儿子都会转移到它的父亲上去。每个点需要满足\(c_i+son(i)\le m\),其中\(son(i)\)是\(i\)节点的儿子个数。求最多可以删掉多少个点。
\(n\le2\times10^6\)
sol
设\(f_i\)表示\(i\)点子树内最多可以删多少个点,\(g_i\)表示在\(i\)子树内达成最优策略时,\(i\)号点的\(c_i+son(i)\)是多少。
对于一个点\(u\),考虑它的所有儿子删还是不删。一个贪心的结论是:按照\(g_v\)从小到大能删则删。因为删除一个儿子\(v\)只会增大\(u\)的\(g_u\)导致\(u\)可能删不掉,所以这样删一定是不会使答案更劣的。
code
辣鸡\(\mbox{bzoj}\)不支持\(\mbox{-std=c++11}\)
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<vector>
using namespace std;
int gi(){
int x=0,w=1;char ch=getchar();
while ((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
if (ch=='-') w=0,ch=getchar();
while (ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
return w?x:-x;
}
const int N = 2e6+5;
int n,m,f[N],g[N],tmp[N];
vector<int>edge[N];
void dfs(int u){
for (auto v:edge[u]) dfs(v),f[u]+=f[v];
int len=0;
for (auto v:edge[u]) tmp[++len]=g[v]-1;
sort(tmp+1,tmp+len+1);
for (int i=1;i<=len;++i) if (g[u]+tmp[i]<=m) g[u]+=tmp[i],++f[u];
}
int main(){
n=gi();m=gi();
for (int i=1;i<=n;++i) g[i]=gi();
for (int i=1;i<=n;++i){
int k=gi();g[i]+=k;
while (k--) edge[i].push_back(gi()+1);
}
dfs(1);printf("%d\n",f[1]);
return 0;
}