[APIO2012]派遣 左偏树

P1552 [APIO2012]派遣

题面

考虑枚举每个节点作为管理者,计算所获得的满意程度以更新答案。对于每个节点的计算,贪心,维护一个大根堆,每次弹出薪水最大的人。这里注意,一旦一个人被弹出,那么不再可能出现在其祖先们的最优解里(废话),所以使用可并堆左偏树优化复杂度。

#include <cstdio>
#include <algorithm>
#define MAXN 100010
#define MAX(A,B) ((A)>(B)?(A):(B))
#define LL long long
using namespace std;
int n,m,rot;
LL ans;
int head[MAXN],nxt[MAXN*2],vv[MAXN*2],tot;
inline void add_edge(int u, int v){
    vv[++tot]=v;
    nxt[tot]=head[u];
    head[u]=tot;
}
int val[MAXN],lead[MAXN],sl[MAXN],sr[MAXN],dis[MAXN];
int merge(int a, int b){
    if(a==0||b==0) return a+b;
    if(val[a]<val[b]) swap(a, b);
    sr[a]=merge(sr[a], b);
    if(dis[sl[a]]<dis[sr[a]]) swap(sl[a], sr[a]);
    dis[a]=dis[sr[a]]+1;
    return a;
}
int root[MAXN],cnt,sz[MAXN],sum[MAXN];
void dfs(int u){
    root[u]=u;sz[u]=1;sum[u]=val[u];
    for(int i=head[u];i;i=nxt[i]){
        int v=vv[i];
        dfs(v);
        root[u]=merge(root[u], root[v]);
        sum[u]+=sum[v];
        sz[u]+=sz[v];
        while(sum[u]>m){
            sum[u]-=val[root[u]];
            sz[u]-=1;
            root[u]=merge(sl[root[u]], sr[root[u]]);
        }
    }
    ans=MAX(ans, (LL)sz[u]*lead[u]);
}
int main()
{
    scanf("%d %d", &n, &m);
    for(int i=1;i<=n;++i){
        int v;scanf("%d", &v);
        if(v==0) rot=i;
        add_edge(v,i);
        scanf("%d %d", &val[i], &lead[i]);
    }
    dfs(rot);
    printf("%lld", ans);
    return 0;
}
posted @ 2019-06-17 13:47  Santiego  阅读(150)  评论(0编辑  收藏  举报