一道简单的贪心题。很显然是维护一个数据结构,从叶子向根逐级合并,并随时更新答案。 我是用斜堆的,代码量会比splay启发式合并要小一些。
斜堆可以参考wiki:click here
每个节点维护一个大根堆,代表这个点子树的薪水。这里可以用斜堆/左偏树/二项堆/Fibonacci堆…… 从叶子向根部逐级合并, 每次合并之后,若这个点的子树总薪水大于预算m,就一个一个删除堆顶元素。 直到堆中元素和小于等于m。
斜堆每次合并复杂度均摊log(n),总共合并n次。 这部分的复杂度就是nlogn。 每删除一次就要合并一次斜堆的左右儿子,因此每次删除的复杂度也是均摊log(n) ,而每个点最多删除一次,总共最多删除n个点。 这部分的复杂度也是nlogn。
#include <iostream>
#include <cstdio>
#include <queue>
#define next nextt
#define MAXN 200000
using namespace std;
typedef long long LL;
struct node
{
node *l,*r;
LL val;
node() {}
}*tree[MAXN],pool[MAXN];
int ind[MAXN],next[MAXN];
LL lead[MAXN],m,ans,sum[MAXN],num[MAXN];
int n;
queue<int> q;
node* merge(node *a,node *b)
{
if (!a||!b) return a?a:b;
if (b->val>a->val) swap(a,b);
a->r=merge(a->r,b);
swap(a->l,a->r);
return a;
}
void del(int p)
{
num[p]--;
sum[p]-=tree[p]->val;
tree[p]=merge(tree[p]->l,tree[p]->r);
}
int main()
{
cin>>n>>m;
for (int i=1;i<=n;++i)
{
scanf("%d%lld%lld",&next[i],&pool[i].val,&lead[i]);
++ind[next[i]];
pool[i].l=pool[i].r=NULL;
tree[i]=&pool[i];
sum[i]=pool[i].val;
num[i]=1;
if (lead[i]>ans) ans=lead[i];
}
for (int i=1;i<=n;++i) if (!ind[i]) q.push(i);
while (!q.empty())
{
int u=q.front();q.pop();
int v=next[u];
ind[v]--;
tree[v]=merge(tree[v],tree[u]);
sum[v]+=sum[u]; num[v]+=num[u];
if (!ind[v])
{
while (sum[v]>m) del(v);
if (num[v]*lead[v]>ans) ans=num[v]*lead[v];
if (next[v]!=0)
q.push(v);
}
}
cout<<ans<<endl;
return 0;
}