鏈接:
#include <stdio.h>
int main()
{
puts("轉載請註明出處[vmurder]謝謝");
puts("網址:blog.csdn.net/vmurder/article/details/45315019");
}
題解:
貪心策略步驟一:
如果有多個兒子,那麼顯然(這裏是真的顯然,真的不給證明了)我們肯定要先合併小兒子後合併大兒子。
貪心策略步驟二:
因爲所有節點的載重是相同的,所以我們要先合併葉子節點,不能合併就把父親的權值+1然後葉子就可以去掉啦~(若父親要被合併上去,那麼爺爺就會多出若干被計數爲1的兒子)。
證明1:
爲什麼一定先合併葉子?
因爲:
1.如果合併完父親葉子還能合併,那麼無所謂順序,可以先合併葉子。
2.如果合併完父親葉子並不能合併啦,那麼葉子開始不能合併的情況被考慮到了,沒有問題,而能合併的情況則會發現反正對答案的貢獻都是1,不妨合併葉子。
3.如果不能合併父親,那麼葉子自然要儘量合併啦。
懶,所以只給一下感性理解。
其它證明:
呃那個+1是有講究的。可以分別考慮父親能或者不能被合併到爺爺上的情況~
代碼:
#include <queue>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define N 2001000
using namespace std;
struct Eli
{
int v,next;
}e[N<<2];
int head[N],cnt;
inline void add(int u,int v)
{
e[++cnt].v=v;
e[cnt].next=head[u];
head[u]=cnt;
}
int val[N],n,m;
struct S
{
int x;
S(int _=0):x(_){}
bool operator < (const S &A)const
{return val[x]>val[A.x];}
};
priority_queue<S>q;
int ans,d[N];
void dfs(int x)
{
int i,v;
for(i=head[x];i;i=e[i].next)dfs(e[i].v);
for(i=head[x];i;i=e[i].next)q.push(S(e[i].v));
while(!q.empty())
{
v=q.top().x,q.pop();
if(val[x]+val[v]-1<=m)val[x]+=val[v]-1,ans++;
}
}
int main()
{
freopen("test.in","r",stdin);
int i,j,k;
scanf("%d%d",&n,&m);
for(i=1;i<=n;i++)scanf("%d",&val[i]);
for(i=1;i<=n;i++)
{
scanf("%d",&k);
while(k--)
{
scanf("%d",&j),j++,val[i]++;
add(i,j),d[j]=1;
}
}
for(i=1;i<=n;i++)
{
if(!d[i])
{
dfs(i);
break;
}
}
printf("%d\n",ans);
return 0;
}