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
Solution
看看数据范围,大的可以。。貌似只能支持线性做法了。。然后手动计算(log_22000000=21),于是发现还可以套一个log。
考虑贪心。
明显可以看出自底向上进行贪心没有什么明显反例。每次都从父节点的一众儿子中选一个最小的剪掉。可以证明这样做是可以得出最优结果的。
于是每次遍历完一个爹的所有儿子之后把这一堆儿子按照大小排个序就可以了。
Code
#include <iostream>
#include <cstdlib>
#include <cmath>
#include <string>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <queue>
#include <set>
#include <map>
#define re register
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
#define ms(arr) memset(arr, 0, sizeof(arr))
const int inf = 0x3f3f3f3f;
int num,head[2000001],n,m,size[2000001],w[2000001],nw[2000001],f[2000001];
struct po
{
int nxt,to,from;
};
po edge[2000001];
inline char gc(){
static char BUFF[1000000],*S=BUFF,*T=BUFF;
return S==T&&(T=(S=BUFF)+fread(BUFF,1,1000000,stdin),S==T)?EOF:*S++;
}
inline int read()
{
int x=0,c=1;
char ch=' ';
while((ch>'9'||ch<'0')&&ch!='-')ch=gc();
while(ch=='-')c*=-1,ch=gc();
while(ch<='9'&&ch>='0')x=x*10+ch-'0',ch=gc();
return x*c;
}
inline void add_edge(int from,int to)
{
edge[++num].nxt=head[from];edge[num].to=to;head[from]=num;
}
inline void dfs(int u)
{
for(re int i=head[u];i;i=edge[i].nxt){
int v=edge[i].to;
dfs(v);
size[u]++;
f[u]+=f[v];
}
int cnt=0;
for(re int i=head[u];i;i=edge[i].nxt) nw[++cnt]=size[edge[i].to];
sort(nw+1,nw+cnt+1);
for(re int i=1;i<=cnt;i++){
if(nw[i]+size[u]-1<=m) size[u]+=nw[i]-1,f[u]++;
else break;
}
}
int main()
{
n=read();m=read();
for(re int i=1;i<=n;i++) size[i]=read();
for(re int i=1;i<=n;i++){
int t,x;
t=read();
while(t--) x=read(),add_edge(i,x+1);
}
dfs(1);
cout<<f[1];
return 0;
}