bzoj4027: [HEOI2015]兔子与樱花

传送门:http://www.lydsy.com:808/JudgeOnline/problem.php?id=4027

思路:自底向上贪心。

设f[i]表示i的子树中最多能删多少点,

g[i]表示i的子树删去f[i]个点后的重量。

每次给儿子按g[son[i]]排序,贪心地从小到大删,直到不能删。

为什么可以这样做?

因为删下面的比删上面的点不会更差,

如果因为按贪心删了儿子而导致这个点不能再删,那么我们只会损失一个点,就是该点。

而删除儿子至少会删除一个,所以不会亏。

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
const int maxn=2000010,maxm=maxn<<1;
using namespace std;
int n,m,pre[maxm],now[maxn],son[maxm],tot,c[maxn],a[maxn],ans;
bool ok;char ch;
void read(int &x){
	for (ok=0,ch=getchar();!isdigit(ch);ch=getchar()) if (ch=='-') ok=1;
	for (x=0;isdigit(ch);x=x*10+ch-'0',ch=getchar());
	if (ok) x=-x;
}
void add(int a,int b){pre[++tot]=now[a],now[a]=tot,son[tot]=b;}
void dfs(int x){
	for (int y=now[x];y;y=pre[y]) dfs(son[y]);
	int cnt=0;
	for (int y=now[x];y;y=pre[y]) a[++cnt]=c[son[y]];
	sort(a+1,a+1+cnt);
	for (int i=1;i<=cnt;i++){
		if (c[x]+a[i]-1>m) break;
		c[x]+=(a[i]-1),ans++;
	}
}
int main(){
	scanf("%d%d",&n,&m);
	for (int i=1;i<=n;i++) read(c[i]);
	for (int i=1,x,num;i<=n;i++){
		read(num),c[i]+=num;
		for (int j=1;j<=num;j++) read(x),x++,add(i,x);
	}
	dfs(1),printf("%d\n",ans);
	//for (int i=1;i<=n;i++) printf("%d %d %d\n",i,f[i],g[i]);
	return 0;
}


posted @ 2015-10-29 09:50  orzpps  阅读(145)  评论(0编辑  收藏  举报