题目:
题解:
显然对于每一个忍者来说,我们只能选择其子树里的节点,这些节点的薪水和肯定是越小越好呀,那么我们的直接想法就是维护所在子树内权值和<=m的忍者个数,似乎可以用dfs序+主席树维护?
想要把子树收束在一个顶点似乎是不好实现,我们可以转化一下:舍弃以前的顶点,只按照dfs序建树,这样你虽然会使叶子节点收束全局,但你查询的时候用in[i]-1和out[i]一样可以收束
注意这里按照权值建立线段树,但是必须每一个节点一个区间,不能出现一个节点有两个忍者,虽然你可以将ta们乘起来,但你不能处理只选一个忍者的情况
这就要求我们离散的时候相同的也得分出个大小,这样STL就不行了,我们要用原始的数组法
p[i]表示排名为i的在a中的下标,这样b就是我们的离散数组了
代码:
#include <cstdio>
#include <iostream>
#include <algorithm>
#define LL long long
using namespace std;
const int N=100005;
int tot,nxt[N*2],point[N],v[N*2],in[N],out[N],dfn[N],sz,root[N],nn,c[N],b[N],p[N];
struct hh{int l,r,w;LL sum;}t[N*20];LL ans,l[N],m;
int cmp(int x,int y){return c[x]<c[y];}
void addline(int x,int y)
{
++tot; nxt[tot]=point[x]; point[x]=tot; v[tot]=y;
++tot; nxt[tot]=point[y]; point[y]=tot; v[tot]=x;
}
void dfs(int x,int fa)
{
in[x]=++nn; dfn[nn]=x;
for (int i=point[x];i;i=nxt[i])
if (v[i]!=fa) dfs(v[i],x);
out[x]=nn;
}
void insert(int &now,int l,int r,int x,LL v)
{
t[++sz]=t[now]; now=sz;
t[now].w++; t[now].sum+=v;
if (l==r) return;
int mid=(l+r)>>1;
if (x<=mid) insert(t[now].l,l,mid,x,v);
else insert(t[now].r,mid+1,r,x,v);
}
int qurry(int x,int y,int l,int r,LL m)
{
if (l==r)
{
if (t[y].sum-t[x].sum<=m) return t[y].w-t[x].w;
else return 0;
}
int mid=(l+r)>>1;LL sum=t[t[y].l].sum-t[t[x].l].sum;
if (sum<=m) return t[t[y].l].w-t[t[x].l].w+qurry(t[x].r,t[y].r,mid+1,r,m-sum);
else return qurry(t[x].l,t[y].l,l,mid,m);
}
int main()
{
int n,x,s=0,gen;
scanf("%d%lld",&n,&m);
for (int i=1;i<=n;i++)
{
p[i]=i;
scanf("%d%d%lld",&x,&c[i],&l[i]);
if (x==0) gen=i;
else addline(i,x);
}
sort(p+1,p+n+1,cmp);//p[i]排名为i的在a中的下标
for (int i=1;i<=n;i++) b[p[i]]=i;//离散数组
dfs(gen,0);
for (int i=1;i<=n;i++)
{
root[i]=root[i-1];
insert(root[i],1,n,b[dfn[i]],c[dfn[i]]);
}
for (int i=1;i<=n;i++)
{
LL aa=qurry(root[in[i]-1],root[out[i]],1,n,m);
ans=max(ans,l[i]*aa);
}
printf("%lld",ans);
}