题解:
先自底向上枚举管理者x,那么根据题意,我们就要从x的子树中选择尽量多的忍者,且工资总和不超过m
用一个优先队列
到一个点x,就把它的儿子节点的优先队列启发式合并
显然优先选工资低的,那么维护大根堆,不停地删堆顶,直到工资满足预算即可
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
#include <vector>
using namespace std;
#define N 100010
typedef long long LL;
priority_queue<int> que[N];
LL lead[N],c[N],m,ans;
vector<int> g[N];
int root[N],n;
int Merge(int x,int y)
{
int w;
if(que[x].size()<que[y].size()) swap(x,y);
while(!que[y].empty())
{
w=que[y].top();
que[y].pop();que[x].push(w);
c[x]+=w;c[y]-=w;
}
while(!que[x].empty()&&c[x]>m)
{
c[x]-=que[x].top();
que[x].pop();
}
return x;
}
void dfs(int x)
{
int y;
for(int i=0;i<g[x].size();++i)
{
y=g[x][i];
dfs(y);
root[x]=Merge(root[x],root[y]);
}
LL w=lead[x]*que[root[x]].size();
ans=max(ans,w);
}
int main()
{
scanf("%d%lld",&n,&m);
int x,y;
for(int i=1;i<=n;++i)
{
scanf("%d%d%lld",&x,&y,&lead[i]);
root[i]=i;que[i].push(y);c[i]+=y;
if(x) g[x].push_back(i);
}
dfs(1);
printf("%lld\n",ans);
return 0;
}