题目链接
bzoj2809: [Apio2012]dispatching
题解
领导关系形成一棵树,那么答案为(ans=max{L[u] imes k}),其中k代表以u为根的子树中选出的节点数个数(设这些节点为(v_1,v_2cdots v_k)且有(sum_{i=1}^{k}C[v_i]leq M)。
每个节点建一个大根堆,维护薪水值。初始时若自己满足条件就选自己,否则不选。从叶子节点往上推,对于每个节点,将它与它的儿子节点合并。如果当前的选择费用超出了M,就pop出堆里的最大的,一直到不超过M为止。咋维护呢,好像有个东西叫左偏树来着QAQQQQ
动态维护当前选择的节点数num和选择费用sum即可。
代码
#include<cstdio>
#include<algorithm>
using std::swap;
inline int read() {
int x = 0,f = 1;
char c = getchar();
while(c < '0' || c > '9') {if(c == '-')f = -1;c = getchar(); }
while(c <= '9' && c >= '0')x = x * 10 + c - '0',c = getchar();
return x * f;
}
#define int long long
int n,m;
const int maxn = 200007;
int c[maxn],b[maxn],l[maxn];
int num = 0,head[maxn];
struct node {
int v,next;
}edge[maxn << 1];
void add_edge(int u ,int v) {
edge[++num].v = v,edge[num].next = head[u],head[u] = num;
}
struct Leftist_Tree {
int lc,rc,dis,val;
bool operator < (const Leftist_Tree &q) const {
return val > q.val;
}
} t[maxn];
int fa[maxn];
#define lc t[x].lc
#define rc t[x].rc
int merge(int x,int y) {
if(!x || !y) return x + y;
if(t[y] < t[x]) swap(x,y);
if(t[y].val == t[x].val && x > y) swap(x,y);
fa[rc = merge(rc,y)] = x;
if(t[lc].dis < t[rc].dis) swap(lc,rc);
t[x].dis = t[rc].dis + 1;
return x ;
}
int pop(int x) {
fa[lc] = fa[rc] = 0;
int ret = merge(lc,rc);
lc = rc = 0;
return ret;
}
#undef lc
#undef rc
int sum[maxn],size[maxn],root[maxn],ans;
void dfs(int u) {
root[u] = ++num;
t[num].val = c[u];
size[u] = 1;sum[u] = c[u];
for(int i = head[u]; i;i = edge[i].next) {
int v = edge[i].v;
dfs(v);
sum[u] += sum[v];
size[u] += size[v];
root[u] = merge(root[u],root[v]);
}
while(sum[u] > m) {
sum[u] -= t[root[u]].val;
root[u] = pop(root[u]);
size[u] --;
}
ans = std::max(ans,l[u] * size[u]);
return ;
}
main() {
n = read(),m = read() ;
t[0].dis = -1;
int rt = 0;
for(int i = 1;i <= n;++ i) {
b[i] = read(), c[i] = read(),l[i] = read();
if(b[i]) add_edge(b[i],i);
else rt = i;
}
num = 0;
dfs(rt);
printf("%lld
",ans);
return 0;
}