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