p1552 [APIO2012]派遣

传送门

分析

首先这个题有两个坑点

一是一个点不管可以由父亲领导,任何祖宗均可领导

而是根节点的花费要算在总费用中且它自己也算在总节点数量中

于是我们考虑如何求答案

首先我们知道对于一个点如果在一个子树中就没有选则在更大的一棵子树中一定不会选

因为一棵大的子树有更多选择,结果肯定不会比它的子树劣

于是我们可以每个节点开一个优先队列,每次将超过范围的点直接删掉

于是合并两棵子树是暴力启发式合并即可

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<cctype>
#include<cmath>
#include<cstdlib>
#include<queue>
#include<ctime>
#include<vector>
#include<set>
#include<map>
#include<stack>
using namespace std;
#define int long long
vector<int>v[100100];
int c[100100],l[100100],now[100100],d[100100],sum[100100],n,m,Ans;
priority_queue<int>q[100100];
inline void mer(int u,int x,int y){
    if(q[x].size()<q[y].size()){
      swap(x,y);
      now[u]=x;
    }
    while(!q[y].empty()){
      q[x].push(q[y].top());
      q[y].pop();
    }
}
inline void del(int x){
    while(sum[x]>m){
      sum[x]-=q[now[x]].top();
      q[now[x]].pop();
    }
}
inline void work(int x){
    now[x]=x;
    q[now[x]].push(c[x]);
    sum[x]+=c[x];
    for(int i=0;i<v[x].size();i++){
      work(v[x][i]);
      sum[x]+=sum[v[x][i]];
      mer(x,now[x],now[v[x][i]]);
    }
    del(x);
    int y=q[now[x]].size();
    if(y*l[x]>Ans)Ans=y*l[x];
}
signed main(){
    int i,j,k;
    scanf("%lld%lld",&n,&m);
    for(i=1;i<=n;i++){
      scanf("%lld%lld%lld",&k,&c[i],&l[i]);
      if(k){
        v[k].push_back(i);
        d[i]++;
      }
    }
    for(i=1;i<=n;i++)
      if(!d[i])work(i);
    cout<<Ans;
    return 0;
}
原文地址:https://www.cnblogs.com/yzxverygood/p/10353088.html