LP1552 [APIO2012]派遣 [贪心/可并堆]

ajcxsu
ajcxsu 2018年06月18日
  • 28 次阅读

很久以前看到没深想
今天想做可并堆的练手题顺着标签找到这题
发现本题还算不是很难...

Problem

在这个帮派里,有一名忍者被称之为Master。除了Master以外,每名忍者都有且仅有一个上级。为保密,同时增强忍者们的领导力,所有与他们工作相关的指令总是由上级发送给他的直接下属,而不允许通过其他的方式发送。

现在你要招募一批忍者,并把它们派遣给顾客。你需要为每个被派遣的忍者支付一定的薪水,同时使得支付的薪水总额不超过你的预算。另外,为了发送指令,你需要选择一名忍者作为管理者,要求这个管理者可以向所有被派遣的忍者发送指令,在发送指令时,任何忍者(不管是否被派遣)都可以作为消息的传递人。管理者自己可以被派遣,也可以不被派遣。当然,如果管理者没有被排遣,你就不需要支付管理者的薪水。

你的目标是在预算内使顾客的满意度最大。这里定义顾客的满意度为派遣的忍者总数乘以管理者的领导力水平,其中每个忍者的领导力水平也是一定的。

写一个程序,给定每一个忍者i的上级Bi,薪水Ci,领导力Li,以及支付给忍者们的薪水总预算M,输出在预算内满足上述要求时顾客满意度的最大值。

Solution

如果确定了一个上级,那么它能招募的人也就确定了。枚举上级,我们所需要做的就是使招募的人尽量多。

维护一个可并大根堆维护薪水最大值,递归往下枚举上级。

复杂度$O(n\log n)$。

有趣的是我一开始以为上级是必须选的

如果搞可并对顶堆的话,复杂度不好保证

考虑优化,上面要耗的钱比下面的少的话下面做上级就肯定亏了

所以从下面往上走耗的钱肯定越来越多

于是限制具有单调性,就不需要对顶堆了

到最后才发现题目理解错了,还用了很傻比的写法233

Code

// Code by ajcxsu
// Problem: ninja

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;

const int N=1e5+10, M=2e5+10;

int h[N], to[M], nexp[M], p=1;
inline void ins(int a, int b) {
    nexp[p]=h[a], h[a]=p, to[p]=b, p++;
}

struct Node *nil;
struct Node {
    ll val, tot, siz;
    Node *ls, *rs;
    Node(ll val=0x3f3f3f3f3f3f3fll): val(val), tot(val) { ls=rs=nil; siz=1; }
} ;
void ini() {
    nil=new Node();
    nil->ls=nil->rs=nil, nil->siz=0;
}

Node* merge(Node *x, Node *y) {
    if(x==nil) return y;
    if(y==nil) return x;
    if(x->val < y->val) swap(x, y);
    y->rs=x->ls, x->ls=y;
    x->tot+=y->tot, x->siz+=y->siz;
    return x;
}

Node* merges(Node *x) {
    if(x==nil || x->rs==nil) return x;
    Node *a=x->rs, *b=a->rs;
    x->rs=a->rs=nil;
    return merge(merge(x, a), merges(b));
}

Node* del(Node *x) {
    Node *y=merges(x->ls);
    y->tot=x->tot-x->val;
    y->siz=x->siz-1;
    return y;
}

inline void gn(int &x) {
    x=0;
    char ch=getchar();
    while(ch<'0' || ch>'9') ch=getchar();
    while(ch>='0' && ch<='9') x=x*10+ch-'0', ch=getchar();
}

int n,m;
int w[N], c[N];
ll ans=0;

Node* dfs(int x) {
    Node *nd=new Node(c[x]);
    for(int u=h[x];u;u=nexp[u])
        nd=merge(nd, dfs(to[u]));
    while(nd->tot>m) {
        nd=del(nd);
    }
    ans=max(ans, 1ll*nd->siz*w[x]);
    return nd;
}

int main() {
    ini();
    gn(n), gn(m);
    int fa, root;
    for(int i=1;i<=n;i++) {
        gn(fa), gn(c[i]), gn(w[i]);
        if(!fa) root=i;
        else ins(fa, i);
    }
    dfs(root);
    printf("%lld\n", ans);
    return 0;
}

本文链接:https://acxblog.site/archives/sol-luogu-1552.html
本文采用 CC BY-NC-SA 3.0 协议进行许可

    菜鸡7号
    菜鸡7号  2018-08-14, 22:25

    sto老余太巨了orz,蒟蒻先吐一口血为敬。。。