[BZOJ4027][HEOI2015] 兔子与樱花

时间:2022-10-27 16:49:37

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。
 
 
题解:
  树形动态规划+贪心。对于任意一个节点,它当前的权值为c[i]+son[i]。假设我们删除了它的某一个儿子节点j,则权值增加c[j]+son[j]-1。那么显然,我们在删除节点的时候,应该从权值最小的到最大的依次进行,直到当前节点的权值已经超过限重。
  其实这种思路应该是算不上动态规划的,最重要的还是贪心部分。全过程直接用DFS就可以完成。建议加读入优化,2*10^6的数据不是盖的。
 
代码:
----------------------------------------------------------------------------------------------------

#include <cstdio>
#include <algorithm>
#define MAXN 2000005
using namespace std;

struct Edge { int v, next; } edge[MAXN];

int w[MAXN], son[MAXN], n, m, now, h[MAXN], v, f[MAXN], st[MAXN];

int read()
{
  int res = 0; char ch = getchar();
  while (ch < '0' && ch > '9') ch = getchar();
  while (ch >= '0' && ch <= '9') res = res * 10 + ch - '0', ch = getchar();
  return res;
}

int DFS(int o)
{
  int cnt = 0;
  for (int x = h[o]; x; x = edge[x].next)
  {
    int v = edge[x].v;
    DFS(v), w[o]++, f[o] += f[v];
  }
  for (int x = h[o]; x; x = edge[x].next) st[++cnt] = w[edge[x].v];
  sort(st + 1, st + cnt + 1);
  for (int i = 1; i <= cnt; i++)
    if (w[o] + st[i] - 1 <= m) w[o] += st[i] - 1, f[o]++; else break;
  return f[o];
}

int main()
{
  freopen("4027.in", "r", stdin);
  freopen("4027.out", "w", stdout);
  n = read(), m = read();
  for (int i = 0; i <= n - 1; i++) w[i] = read();
  for (int i = 0; i <= n - 1; i++)
  {
    son[i] = read();
    for (int j = 1; j <= son[i]; j++) v = read(), now++, edge[now] = (Edge) {v, h[i]}, h[i] = now;
  }
  printf("%d", DFS(0));
  return 0;
}

----------------------------------------------------------------------------------------------------