题意:给定一棵n个点的树,每个点有各自的代价和价值,对以每个点为根的子树里最多能选多少点使得这些点的代价和不超过限制
乍一看是维护代价小根堆,能选小的就选小的,然而复杂度原地爆炸
那么换个角度,一开始对一个子树全选,最少丢弃多少点使得它们的代价和不超过限制
显然是维护一个代价的大根堆
我们就从下往上进行处理
显然我们需要资磁合并、删除、快速找到代价最大的点
显然左偏树
uses math;
var
n,m,x,ll,root :longint;
last,pre,other :array[0..100010] of longint;
i :longint;
l,r,size,dis :array[0..100010] of longint;
sum,cost,v :array[0..100010] of int64;
ans :int64;
procedure swap(var a,b:longint);
var
c:longint;
begin
c:=a; a:=b; b:=c;
end;
procedure connect(x,y:longint);
begin
inc(ll);
pre[ll]:=last[x];
last[x]:=ll;
other[ll]:=y;
end;
function combine(x,y:longint):longint;
begin
if (x=0) or (y=0) then exit(x+y);
if cost[x]<cost[y] then swap(x,y);
r[x]:=combine(r[x],y);
sum[x]:=sum[l[x]]+sum[r[x]]+cost[x];
size[x]:=size[l[x]]+size[r[x]]+1;
if dis[l[x]]<dis[r[x]] then swap(l[x],r[x]);
dis[x]:=dis[r[x]]+1;
exit(x);
end;
function work(x:longint):longint;
var
p,q,rt:longint;
begin
sum[x]:=cost[x];
size[x]:=1;
rt:=x;
q:=last[x];
while q<>0 do
begin
p:=other[q];
rt:=combine(work(p),rt);
q:=pre[q];
end;
while (sum[rt]>m) do rt:=combine(l[rt],r[rt]);
ans:=max(ans,int64(size[rt])*v[x]);
exit(rt);
end;
begin
read(n,m);
for i:=1 to n do
begin
read(x,cost[i],v[i]);
if x<>0 then connect(x,i) else root:=i;
end;
work(root);
writeln(ans);
end.
——by Eirlys