题面
http://darkbzoj.tk/problem/2809
题解
前置知识
可以先按上级-下级的关系建出一棵关系树,然后将在这个树上按dfs的顺序枚举题目中的“管理者”u。容易发现派遣的所有忍者都在u的子树中。而根据贪心思想,显然应该按照薪水从小到大选取u子树中的节点,直到即将超出预算。
对于每一个管理者,维护一个大根堆,里面存的是依照上述过程选出来的所有点的薪水值。那么点u的堆就是u的所有子节点的堆,以及C[u],这些东西全部合并;然后,每次删除最大值,直到堆中总和不超过预算。点u作为管理者对应的最佳答案就是此时堆的大小*L[u]。最后对所有u的答案求最大值即可。
合并堆的过程可以使用左偏树来实现。每个点最多入堆1次,出堆1次。合并总次数O(n)。故总时间复杂度(O(n log n))。
代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define In inline
#define rg register
const ll N = 1e5;
In ll read(){
ll s = 0,ww = 1;
char ch = getchar();
while(ch < '0' || ch > '9'){if(ch == '-')ww = -1;ch = getchar();}
while('0' <= ch && ch <= '9'){s = 10 * s + ch - '0';ch = getchar();}
return s * ww;
}
ll C[N+5],L[N+5];
struct LftTree{
ll cnt,c[N+5][2],val[N+5],dis[N+5],sum[N+5],num[N+5];
void reset(){
dis[0] = -1;
cnt = 0;
}
int create(int i){
cnt++;
num[cnt] = 1;
sum[cnt] = val[cnt] = C[i];
return cnt;
}
int merge(int u,int v){
if(!u || !v)return u + v;
if(val[u] < val[v])swap(u,v);
c[u][1] = merge(c[u][1],v);
if(dis[c[u][0]] < dis[c[u][1]])swap(c[u][0],c[u][1]);
dis[u] = dis[c[u][1]] + 1;
sum[u] = sum[c[u][0]] + sum[c[u][1]] + val[u];
num[u] = num[c[u][0]] + num[c[u][1]] + 1;
return u;
}
int pop(int u){
return merge(c[u][0],c[u][1]);
}
}T;
ll n,m,rt,ans;
ll head[N+5],cnt,root[N+5];
struct node{
int des,next;
}e[N+5];
In void addedge(int a,int b){
cnt++;
e[cnt].des = b;
e[cnt].next = head[a];
head[a] = cnt;
}
void dfs(int u){
root[u] = T.create(u);
for(rg int i = head[u];i;i = e[i].next){
int v = e[i].des;
dfs(v);
root[u] = T.merge(root[u],root[v]);
}
while(T.sum[root[u]] > m)root[u] = T.pop(root[u]);
ans = max(ans,L[u] * T.num[root[u]]);
}
int main(){
n = read(),m = read();
T.reset();
for(rg int i = 1;i <= n;i++){
ll fa = read();
if(!fa)rt = i;
else addedge(fa,i);
C[i] = read(),L[i] = read();
}
dfs(rt);
cout << ans << endl;
return 0;
}