传送门
1° 主席树
枚举每一个忍者当管理者,由于选忍者的顺序满足薪水单调不减,可以用dfs序+主席树维护出子树中权值之和小于等于k最多有多少个。维护的时候需要维护sum权值和以及s满足条件的忍者的个数。注意建的是权值线段树。
2° 可并堆
3°splay+启发式合并
主席树
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
#define LL long long
const int max_n=1e5+5;
const int max_e=max_n*2;
const int max_tree=max_n*20;
int n,fa,sz,N,p[max_n],a[max_n],root[max_n],num[max_n],num1[max_n],in[max_n],out[max_n],val[max_n],ls[max_tree],rs[max_tree];
int tot,point[max_n],next[max_e],v[max_e];
LL k,kk,calc,calck,power[max_n],ans,sum[max_tree],s[max_tree];
inline int cmp(int x,int y){return a[x]<a[y];}
inline void addedge(int x,int y){++tot; next[tot]=point[x]; point[x]=tot; v[tot]=y;}
inline void dfs(int x){
in[x]=++N; val[N]=a[x]; num[N]=num1[x];
for (int i=point[x];i;i=next[i]) dfs(v[i]);
out[x]=N;
}
inline void build(int &now,int l,int r,int x,int v){
int mid=(l+r)>>1;
sum[++sz]=(LL)sum[now]+(LL)v,s[sz]=s[now]+1,ls[sz]=ls[now],rs[sz]=rs[now],now=sz;
if (l==r) return;
if (x<=mid) build(ls[now],l,mid,x,v);
else build(rs[now],mid+1,r,x,v);
}
inline int query(int a,int b,int l,int r,LL k){
int mid=(l+r)>>1;
if (l==r){
if (sum[b]-sum[a]!=0&&calck+sum[b]-sum[a]<=kk) return calc+1;
else return calc;
}
LL t=sum[ls[b]]-sum[ls[a]];
if (t>=k) return query(ls[a],ls[b],l,mid,k);
else {calc+=s[ls[b]]-s[ls[a]]; calck+=t; return query(rs[a],rs[b],mid+1,r,k-t);}
}
int main(){
scanf("%d%lld",&n,&k);kk=k;
for (int i=1;i<=n;++i){
p[i]=i;
scanf("%d%d%lld",&fa,&a[i],&power[i]);
if (fa) addedge(fa,i);
}
sort(p+1,p+n+1,cmp);
for (int i=1;i<=n;++i) num1[p[i]]=i;
dfs(1);
root[0]=sz=0;
for (int i=1;i<=n;++i){
root[i]=root[i-1];
build(root[i],1,n,num[i],val[i]);
}
for (int i=1;i<=n;++i){
int l=in[i]-1,r=out[i];
calc=calck=0;
LL cnt=query(root[l],root[r],1,n,k);
if (power[i]*cnt>ans) ans=power[i]*cnt;
}
printf("%lld",ans);
}
1°‘主席树’本来可以1A的题因为%I64d嘿嘿嘿了