【BZOJ4027】【HEOI2015】兔子與櫻花 貪心

鏈接:

#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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章