[HBOI2015]兔子与樱花

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

现在兔子们觉得樱花树上节点太多,希望去掉一些节点。当一个节点被去掉之后,这个节点上的樱花和它的儿子节点都被连到删掉节点的父节点上。如果父节点也被删除,那么就会继续向上连接,直到第一个没有被删除的节点为止。

现在兔子们希望计算在不违背最大载重的情况下,最多能删除多少节点。

注意根节点不能被删除,被删除的节点不被计入载重。

Hint:

对于100%的数据,1 <= n <= 2000000, 1 <= m <= 100000, 0 <= c_i <= 1000

Solution:

考虑贪心,自下而上对每个点从小到大删子节点,能删则删

从小到大很好理解,为什么要从下往上呢

因为删一些子节点,那么可能会损失删这个节点的答案,且最多为1

但你删它的话不就相当于把答案补回来了吗,而且这不会影响到父亲的删或不删

有根树排序小技巧\(get\)

#include <map>
#include <set>
#include <stack>
#include <cmath>
#include <queue>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#define ls p<<1 
#define rs p<<1|1
using namespace std;
typedef long long ll;
const int mxn=2e6+5;
int n,m,cnt,hd[mxn];

inline int read() {
	char c=getchar(); int x=0,f=1;
	while(c>'9'||c<'0') {if(c=='-') f=-1;c=getchar();}
	while(c<='9'&&c>='0') {x=(x<<3)+(x<<1)+(c&15);c=getchar();}
	return x*f;
}
inline void chkmax(int &x,int y) {if(x<y) x=y;}
inline void chkmin(int &x,int y) {if(x>y) x=y;}

struct ed {
	int to,nxt;
}t[mxn<<1];

inline void add(int u,int v) {
	t[++cnt]=(ed) {v,hd[u]}; hd[u]=cnt;
}

int tot,ans,w[mxn],fa[mxn],to[mxn],l[mxn],r[mxn],son[mxn];
int cmp(int x,int y) {
    return w[x]+son[x]<w[y]+son[y];
}

void dfs(int u) {
	if(!son[u]) return ;
	for(int i=l[u];i<=r[u];++i) {
		int v=to[i];
		dfs(v); 
	}
	sort(to+l[u],to+r[u]+1,cmp);
	for(int i=l[u];i<=r[u];++i) {
		int v=to[i];
		if(w[u]+son[u]+w[v]+son[v]-1<=m) 
			w[u]+=w[v],son[u]+=son[v]-1,++ans;
		else break ;
	}
}

int main()
{
	n=read(); m=read();	int k,x;
	for(int i=1;i<=n;++i) w[i]=read();
	for(int i=1;i<=n;++i) {
		k=read(); l[i]=tot+1; r[i]=tot+k; son[i]=k;
		while(k--) ++tot,to[tot]=read()+1;
	}
	dfs(1); printf("%d",ans);
    return 0;
}

posted @ 2019-03-27 11:49  cloud_9  阅读(165)  评论(0编辑  收藏  举报