题目链接:2809:[Apio2012]dispatching
考虑对于以节点x为根的一颗子树,我们只要在m的限制下尽量多地选择忍者即可,这个可以用大根堆维护
考虑x向x的父亲y转移时,相当于y的各颗子树在m的限制下尽可能多的选择忍者,显然我们不能暴力重建堆,用可并堆logn维护即可,这里我用的是左偏树
每转移到一个节点就更新一遍答案
#include<queue>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
#define LL long long
using namespace std;
const int maxn=200010;
int n,m,tot=1,h[maxn],l[maxn];
int rt[maxn],cnt[maxn],root,num=0;
struct edge{int to,next;}G[maxn*6];
LL ans=0,cost[maxn],c[maxn];
struct LeftIstTree{
struct treenode{
int v,l,r,d;
treenode(){v=l=r=d=0;}
bool operator < (const treenode &a)const{return v<a.v;}
};
treenode t[maxn];
int newnode(int x){
num++; t[num].v=c[x]; return num;
}
int merge(int a,int b){
if(!a||!b)return a+b;
if(t[a]<t[b])swap(a,b);
t[a].r=merge(t[a].r,b);
if(t[t[a].l].d<t[t[a].r].d)swap(t[a].l,t[a].r);
t[a].d=t[a].r?t[t[a].r].d+1:0;
return a;
}
}lit;
void add(int x,int y){
G[++tot].to=y;G[tot].next=h[x];h[x]=tot;
}
void dfs(int x){
rt[x]=lit.newnode(x); cost[x]=c[x]; cnt[x]=1;
if (c[x]<=m) ans=max(ans,1LL*l[x]);
for (int i=h[x];i;i=G[i].next){
int v=G[i].to; dfs(v);
rt[x]=lit.merge(rt[x],rt[v]);
cost[x]+=cost[v]; cnt[x]+=cnt[v];
while (cost[x]>m) cost[x]-=lit.t[rt[x]].v,cnt[x]--,
rt[x]=lit.merge(lit.t[rt[x]].l,lit.t[rt[x]].r);
ans=max(ans,1LL*l[x]*cnt[x]);
}
}
int main(){
scanf("%d%d",&n,&m);
for (int i=1;i<=n;++i){
int x; scanf("%d%lld%d",&x,&c[i],&l[i]);
add(x,i); if (!x) root=i;
}
ans=0; dfs(root);
printf("%lld\n",ans);
}