BZOJ2809【APIO2012】Dispatching <可并堆>

Problem

【APIO2012】Dispatching

Time  Limit:  10  Sec\mathrm{Time\;Limit:\;10\;Sec}
Memory  Limit:  128  MB\mathrm{Memory\;Limit:\;128\;MB}

Description

在一个忍者的帮派里,一些忍者们被选中派遣给顾客,然后依据自己的工作获取报偿。
在这个帮派里,有一名忍者被称之为Master\mathrm{Master}。除了Master\mathrm{Master}以外,每名忍者都有且仅有一个上级。为保密,同时增强忍者们的领导力,所有与他们工作相关的指令总是由上级发送给他的直接下属,而不允许通过其他的方式发送。
现在你要招募一批忍者,并把它们派遣给顾客。你需要为每个被派遣的忍者支付一定的薪水,同时使得支付的薪水总额不超过你的预算。另外,为了发送指令,你需要选择一名忍者作为管理者,要求这个管理者可以向所有被派遣的忍者发送指令,在发送指令时,任何忍者(不管是否被派遣)都可以作为消息的传递人。管理者自己可以被派遣,也可以不被派遣。当然,如果管理者没有被排遣,就不需要支付管理者的薪水。你的目标是在预算内使顾客的满意度最大。
这里定义顾客的满意度为派遣的忍者总数乘以管理者的领导力水平,其中每个忍者的领导力水平也是一定的。
写一个程序,给定每一个忍者ii的上级BiB_i,薪水CiC_i,领导力LiL_i,以及支付给忍者们的薪水总预算MM,输出在预算内满足上述要求时顾客满意度的最大值。

Input

从标准输入读入数据。
第一行包含两个整数NNMM,其中NN表示忍者的个数,MM表示薪水的总预算。
接下来NN行描述忍者们的上级、薪水以及领导力。其中的第ii行包含三个整数Bi,Ci,LiB_i,C_i,L_i分别表示第ii个忍者的上级,薪水以及领导力。Master\mathrm{Master}满足Bi=0B_i=0,并且每一个忍者的老板的编号一定小于自己的编号。

Output

输出一个数,表示在预算内顾客的满意度的最大值。

Sample Input

1
2
3
4
5
6
5 4
0 3 3
1 3 5
2 2 2
1 2 4
2 3 1

Sample Output

1
6

Hint

样例解释
如果我们选择编号为11的忍者作为管理者并且派遣第三个和第四个忍者,薪水总和为44,没有超过总预算44
因为派遣了22个忍者并且管理者的领导力为33,用户的满意度为66,是可以得到的用户满意度的最大值。
数据范围
1N105,  1M109,  0Bi<i,  1CiM,  1Li1091\le N\le10^5,\;1\le M\le10^9,\;0\le B_i<i,\;1\le C_i\le M,\;1\le L_i\le10^9

标签:可并堆 左偏树

Solution

可并堆基础题。

对于每个结点作领导的情况,贪心策略肯定在其子树中从小往大选,直到选不了为止。可以每个结点用一个堆维护,但遍历子树会导致复杂度爆炸。

考虑每次用已经算出的一些结点的答案。那么可以想到一种做法:
将每个结点子树中的所有点默认先选上,再从大往小去掉直到可行为止。这样在儿子结点中都没选到的点一定不会在父节点中选到。于是可以直接将每个结点最后选出的点加入到父亲的备选点集中。这样不难发现可以用可并堆维护,DFS\mathrm{DFS}时将所有儿子结点的可并堆并起来,再从大往小pop\mathrm{pop}点,找到可行最大sizesize后更新答案即可。

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
#include <bits/stdc++.h>
#define MAX_N 100000
using namespace std;
typedef long long lnt;
template <class T> inline void read(T &x) {
x = 0; int c = getchar(), f = 1;
for (; !isdigit(c); c = getchar()) if (c == 45) f = -1;
for (; isdigit(c); c = getchar()) (x *= 10) += f*(c-'0');
}
struct node {int c, d, ls, rs;} h[MAX_N+5];
int n, m, b[MAX_N+5], c[MAX_N+5], l[MAX_N+5], sz[MAX_N+5];
vector <int> G[MAX_N+5]; int fa[MAX_N+5]; lnt mx, s[MAX_N+5];
int getf(int x) {return fa[x] == x ? fa[x] : getf(fa[x]);}
int merge(int a, int b) {
if (!a || !b) return a^b;
if (h[a].c < h[b].c) swap(a, b);
h[a].rs = merge(h[a].rs, b), fa[h[a].rs] = a;
if (h[h[a].rs].d > h[h[a].ls].d) swap(h[a].ls, h[a].rs);
h[a].d = h[a].rs ? h[h[a].rs].d+1 : 0; return a;
}
int pop(int a) {
int l = h[a].ls, r = h[a].rs;
h[a].ls = h[a].rs = h[a].c = 0;
return fa[l] = l, fa[r] = r, merge(l, r);
}
int DFS(int u) {
int rt = u; s[u] = c[u], sz[u] = 1;
for (int i = 0, v; i < (int)G[u].size(); i++)
rt = merge(rt, DFS(v = G[u][i])), s[u] += s[v], sz[u] += sz[v];
while (s[u] > m && sz[u]) s[u] -= h[rt].c, sz[u]--, rt = pop(rt);
return mx = max(mx, 1LL*sz[u]*l[u]), rt;
}
int main() {
read(n), read(m);
for (int i = 1; i <= n; i++)
read(b[i]), read(c[i]), read(l[i]);
for (int i = 1; i <= n; i++)
G[b[i]].push_back(i);
for (int i = 1; i <= n; i++)
fa[i] = i, h[i].c = c[i];
return DFS(1), printf("%lld\n", mx), 0;
}