BZOJ 4027: [HEOI2015]兔子与樱花

Description

很久很久之前,森林里住着一群兔子。有一天,兔子们突然决定要去看樱花。兔子们所在森林里的樱花树很特殊。樱花树由n个树枝分叉点组成,编号从 0 n1,这 n 个分叉点由n1个树枝连接,我们可以把它看成一个有根树结构,其中 0 号节点是根节点。这个树的每个节点上都会有一些樱花,其中第i个节点有 ci 朵樱花。樱花树的每一个节点都有最大的载重 m ,对于每一个节点i,它的儿子节点的个数和i节点上樱花个数之和不能超过 m ,即son(i)+ci<=m,其中 son(i) 表示i的儿子的个数,如果 i 为叶子节点,则son(i)=0

现在兔子们觉得樱花树上节点太多,希望去掉一些节点。当一个节点被去掉之后,这个节点上的樱花和它的儿子节点都被连到删掉节点的父节点上。如果父节点也被删除,那么就会继续向上连接,直到第一个没有被删除的节点为止。
现在兔子们希望计算在不违背最大载重的情况下,最多能删除多少节点。
注意根节点不能被删除,被删除的节点不被计入载重。

Solution

诶竟然1A了,不可思议。
这个贪心感觉很奇怪。。
首先如果 v 和它的一个子节点t合并了,那么这个节点的载重变成了 c[v]+c[t]+son[v]son[t]1 ,那么我们按照 c[t]son[t] 排序来更新点即可咯。
还有一件事就是如果点 A 可以删B,点 B 可以删C,但 B 删了C就不可以和 A <script type="math/tex" id="MathJax-Element-25">A</script>并了,该怎么决策?
反正都是缩一个,观察,若缩下面,就这样,但缩上面,可能干扰其它点缩,并且下面的也不可能再缩上去,亏了,所以缩下面即可。

那么就是一遍dfs可以搞定的事情了。

Code

/**************************************************************
    Problem: 4027
    User: bblss123
    Language: C++
    Result: Accepted
    Time:4696 ms
    Memory:61080 kb
****************************************************************/

#include<iostream>
#include<string.h>
#include<stdio.h>
#include<algorithm>
#include<stdlib.h>
#include<time.h>
#include<math.h>
#include<queue>
#include<vector>
#include<map>
#include<set>
using namespace std;
#define pb push_back
#define ph push
#define vecn vector<node>
#define pqn priority_queue<node>
#define debug cout<<#x<<" "<<x<<endl;
#define frt fisrt
#define snd second
typedef double db;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> pii;
typedef vector<int> vec;
typedef priority_queue<int> pq;
const int INF=1<<30;
const ll inf=1ll<<60;
const int M=2e6+5;
inline void Max(int &a,int b){if(a<b)a=b;}
inline void Min(int &a,int b){if(a>b||a==-1)a=b;}
inline void rd(int &a){
    a=0;char c;
    while(c=getchar(),!isdigit(c));
    do a=a*10+(c^48);
        while(c=getchar(),isdigit(c));
}
inline void work(ll x){
    if(!x)return;
    work(x/10);
    putchar(48+x%10);
}
inline void print(ll x){
    if(!x)putchar('0');
    else work(x);
}
int n,m,c[M],son[M],ans;
vec G[M];
struct node{
    int val,x;
    inline bool operator < (const node &tmp)const{
        return val>tmp.val;
    }
};
inline void dfs(int v){
    pqn que;for(;!que.empty();)que.pop();
    for(int i=0;i<G[v].size();++i){
        int to=G[v][i];
        dfs(to);
        que.push((node){c[to]+son[to],to});
    }
    for(;!que.empty();){
        node u=que.top();que.pop();
        if(c[v]+son[v]+u.val-1<=m){
            c[v]+=c[u.x],son[v]+=son[u.x]-1;
            ++ans;
        }
        else break;
    }
}
//////////////////////////////////////////////////////////////////////////////////////////////
//#define LOCAL
//////////////////////////////////////////////////////////////////////////////////////////////
int main(){
#ifdef LOCAL
    freopen("data.in","r",stdin);
    freopen("data.out","w",stdout);
#endif
    cin>>n>>m;
    for(int i=0;i<n;++i)
        rd(c[i]);
    for(int i=0;i<n;++i){
        rd(son[i]);
        for(int j=1,a;j<=son[i];++j)
            rd(a),G[i].pb(a);
    }
    dfs(0);
    cout<<ans<<endl;
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值