1. 程式人生 > 實用技巧 >P4107 [HEOI2015]兔子與櫻花 貪心

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;
}