P4107 [HEOI2015]兔子与樱花 贪心
题目描述
分析
一道贪心题
首先我们可以证明最优的贡献一定是从下依次合并到上的
不会出现一个节点不能合并到父亲节点,却能合并到父亲节点的祖先节点的情况
我们设当前的节点为 \(u\),\(u\) 的父亲节点为 \(v\),\(v\) 的父亲节点是 \(fa\)
如果 \(u\) 不能合并到 \(v\) 上,那么必定有
\(c[u]+son[u]-1+c[v] +son[v]>m\)
如果我们把 \(v\) 合并到 \(fa\) 上再把 \(u\) 合并到 \(fa\) 上
那么 \(fa\) 此时的值为
\(c[fa]+son[fa]-1+c[u]+son[u] -1+c[v]+son[v]\)
我们发现右半部分一定是大于 \(m\) 的
因此此时 \(fa\) 的值一定大于 \(m\)
所以合并的过程一定是从下到上依次进行的
不会出现将某个节点合并到父亲节点后又将该节点的儿子节点合并到其父亲节点上
所以我们可以一边 \(dfs\) 求出答案
对于每一个节点将其所有儿子节点对它的贡献从小到大排序,依次合并,直到不能合并为止
某个节点 \(u\) 对其父亲节点 \(fa\) 的贡献为 \(son[u]+c[u]-1\)
代码
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#define rg register
const int maxn=2e6+5;
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;
}
int h[maxn],tot=1;
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 son[maxn],c[maxn],n,m,ans,js[maxn];
void dfs(int now,int fa){
std::vector<int> g;
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);
g.push_back(js[u]-1);
}
std::sort(g.begin(),g.end());
rg int haha=g.size()-1;
for(rg int i=0;i<=haha;i++){
if(js[now]+g[i]<=m){
js[now]+=g[i];
ans++;
}
}
g.clear();
}
int main(){
memset(h,-1,sizeof(h));
n=read(),m=read();
rg int aa;
for(rg int i=1;i<=n;i++){
c[i]=read();
}
for(rg int i=1;i<=n;i++){
son[i]=read();
for(rg int j=1;j<=son[i];j++){
aa=read();
aa++;
ad(aa,i),ad(i,aa);
}
js[i]=son[i]+c[i];
}
dfs(1,0);
printf("%d\n",ans);
return 0;
}