大概题意就是,所有忍者有两个属性值分别为薪水值c和领导力值v,有一个预算m,要求找到一位忍者当领导,领导力值为V,并在他的子树里选尽可能多的忍者(他自己可选可不选),数量设为cnt,使得 ,要使最大。
显然的一件事是,当领导确定时,在他子树里最优的取法一定是取c值前cnt小的手下,所以我们想到对于每个节点开一条权值线段树的链,然后从叶子一路合并上去,每到一个节点就在此节点维护的线段树上二分来找到符合条件的最大的cnt。
然而有一个特殊的情况需要注意,就是当二分到权值线段树的叶子节点时发现叶子节点储存的,全取会使。这时就需要把二进制拆分一下,用类似倍增的方法找到这个叶子节点到底取多少个,这样才能保证是最优的。由于是到二分叶子节点只会倍增一次,所以每次查询复杂度还是的,总体复杂度。
最后附上本蒟蒻一份AC代码:
#include<cstdio>//CindyMarshall
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cmath>
using namespace std;
#define N 100005
#define ll long long
#define lc (ls[x])
#define rc (rs[x])
int n,m,h[N],nxt[N],to[N],tre,b[N],mx,tot,root;
struct la{int c,v;}w[N];
inline void tu(int x,int y){to[++tre]=y,nxt[tre]=h[x],h[x]=tre;}
int tr[N*20],rt[N],ls[N*20],rs[N*20];
ll sum[N*20],ans;
void ud(int x){
tr[x]=tr[lc]+tr[rc],sum[x]=sum[lc]+sum[rc];
}
void bt(int l,int r,int p,int &x){
if(l>r)return;
if(!x)x=++tot;
if(l==r&&l==p){tr[x]=1,sum[x]=b[l];return;}
int mid=(l+r)>>1;
if(p<=mid)bt(l,mid,p,lc);
else bt(mid+1,r,p,rc);
ud(x);
}
int merge(int l,int r,int x,int y){
if(!x)return y;if(!y)return x;
if(l==r){tr[x]+=tr[y],sum[x]+=sum[y];return x;}
int mid=(l+r)>>1;
ls[x]=merge(l,mid,ls[x],ls[y]);
rs[x]=merge(mid+1,r,rs[x],rs[y]);
ud(x);
return x;
}
int erfen(int l,int r,int x,ll now,int cnt){
if(l==r){
if(now+sum[x]<=m)cnt+=tr[x];
else{ll tmp=now;
for(int j=log2(tr[x]);j>=0;--j){
if((1<<j)*b[l]+tmp<=m)cnt+=(1<<j),tmp+=(1<<j)*b[l];
}
}
return cnt;
}
int mid=(l+r)>>1;
if(now+sum[lc]>=m)return erfen(l,mid,lc,now,cnt);
else return erfen(mid+1,r,rc,now+sum[lc],cnt+tr[lc]);
}
void dfs(int u){
for(int i=h[u];i;i=nxt[i]){
dfs(to[i]);
rt[u]=merge(1,mx,rt[u],rt[to[i]]);
}
ans=max(ans,1ll*w[u].v*erfen(1,mx,rt[u],0,0));
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1,d;i<=n;++i){scanf("%d%d%d",&d,&w[i].c,&w[i].v);tu(d,i);b[i]=w[i].c;}
sort(b+1,b+n+1);
mx=unique(b+1,b+n+1)-b-1;
for(int i=1;i<=n;++i)w[i].c=lower_bound(b+1,b+mx+1,w[i].c)-b;
for(int i=1;i<=n;++i)bt(1,mx,w[i].c,rt[i]);
dfs(1);
printf("%lld\n",ans);
return 0;
}