1. 程式人生 > >【BZOJ4027】[HEOI2015]兔子與櫻花 貪心

【BZOJ4027】[HEOI2015]兔子與櫻花 貪心

註意 getchar des clas -s 裏的 多少 content 一個數

【BZOJ4027】[HEOI2015]兔子與櫻花

Description

很久很久之前,森林裏住著一群兔子。有一天,兔子們突然決定要去看櫻花。兔子們所在森林裏的櫻花樹很特殊。櫻花樹由n個樹枝分叉點組成,編號從0到n-1,這n個分叉點由n-1個樹枝連接,我們可以把它看成一個有根樹結構,其中0號節點是根節點。這個樹的每個節點上都會有一些櫻花,其中第i個節點有c_i朵櫻花。櫻花樹的每一個節點都有最大的載重m,對於每一個節點i,它的兒子節點的個數和i節點上櫻花個數之和不能超過m,即son(i) + c_i <= m,其中son(i)表示i的兒子的個數,如果i為葉子節點,則son(i) = 0

現在兔子們覺得櫻花樹上節點太多,希望去掉一些節點。當一個節點被去掉之後,這個節點上的櫻花和它的兒子節點都被連到刪掉節點的父節點上。如果父節點也被刪除,那麽就會繼續向上連接,直到第一個沒有被刪除的節點為止。 現在兔子們希望計算在不違背最大載重的情況下,最多能刪除多少節點。 註意根節點不能被刪除,被刪除的節點不被計入載重。

Input

第一行輸入兩個正整數,n和m分別表示節點個數和最大載重

第二行n個整數c_i,表示第i個節點上的櫻花個數 接下來n行,每行第一個數k_i表示這個節點的兒子個數,接下來k_i個整數表示這個節點兒子的編號

Output

一行一個整數,表示最多能刪除多少節點。

Sample Input

10 4
0 2 2 2 4 1 0 4 1 1
3 6 2 3
1 9
1 8
1 1
0
0
2 7 4
0
1 5
0

Sample Output

4

HINT

對於100%的數據,1 <= n <= 2000000, 1 <= m <= 100000, 0 <= c_i <= 1000

數據保證初始時,每個節點櫻花數與兒子節點個數之和大於0且不超過m

題解:容易發現,先刪除深度大的點一定不會比先刪除深度小的點更差,所以從下往上,能刪就刪,優先刪載重小的就行了

#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn=2000010;
int n,m,ans,cnt;
int to[maxn],next[maxn],head[maxn],v[maxn],f[maxn],p[maxn];
int rd()
{
	int ret=0,f=1;	char gc=getchar();
	while(gc<‘0‘||gc>‘9‘)	{if(gc==‘-‘)f=-f;	gc=getchar();}
	while(gc>=‘0‘&&gc<=‘9‘)	ret=ret*10+gc-‘0‘,gc=getchar();
	return ret*f;
}
void add(int a,int b)
{
	to[cnt]=b,next[cnt]=head[a],head[a]=cnt++;
}
bool cmp(int a,int b)
{
	return f[a]<f[b];
}
void dfs(int x)
{
	int i;
	for(i=head[x];i!=-1;i=next[i])	dfs(to[i]);
	for(p[0]=0,i=head[x];i!=-1;i=next[i])	p[++p[0]]=to[i];
	sort(p+1,p+p[0]+1,cmp);
	for(i=1;i<=p[0];i++)	if(f[x]+f[p[i]]-1<=m)	f[x]+=f[p[i]]-1,ans++;
}
int main()
{
	n=rd(),m=rd();
	int i,j,a,b;
	memset(head,-1,sizeof(head));
	for(i=1;i<=n;i++)	v[i]=rd();
	for(i=1;i<=n;i++)
	{
		a=rd(),f[i]=a+v[i];
		for(j=1;j<=a;j++)
		{
			b=rd()+1;
			add(i,b);
		}
	}
	dfs(1);
	printf("%d",ans);
	return 0;
}

【BZOJ4027】[HEOI2015]兔子與櫻花 貪心