正题
题目链接:https://www.luogu.com.cn/problem/P1552
题目大意
一个 个点森林,每个点有价值和代价,选择一个点并在这个点的子树中选择一些点使得。
最大且选择的点的代价之和不超过 。
解题思路
我们对于每个点,我们将所有子节点处理完后将子节点的左偏树合并,然后不停弹出代价最大的点知道代价之和小于等于 即可。
时间复杂度
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
const ll N=110000;
ll n,m,val[N],lead[N],sum[N],ans;
struct Left_Tree{
ll t[N][2],fa[N],dis[N];
#define ls t[x][0]
#define rs t[x][1]
ll Get(ll x)
{return (fa[x]==x)?(x):(fa[x]=Get(fa[x]));}
ll Merge(ll x,ll y){
if(!x||!y) return x+y;
if(val[x]<val[y]||(val[x]==val[y]&&x<y))
swap(x,y);
rs=Merge(rs,y);
if(dis[ls]<dis[rs])swap(ls,rs);
fa[rs]=fa[ls]=x;dis[x]=dis[rs]+1;
sum[x]=sum[rs]+sum[ls]+val[x];
return x;
}
void Del(ll x){
val[x]=0;
fa[ls]=ls;fa[rs]=rs;
fa[x]=Merge(ls,rs);
}
#undef ls
#undef rs
}T;
struct edge_node{
ll to,next;
}a[N];
ll ls[N],tot;
bool k[N];
void addl(ll x,ll y)
{
a[++tot].to=y;
a[tot].next=ls[x];
ls[x]=tot;
}
ll dfs(ll x){
ll siz=1;
for(ll i=ls[x];i;i=a[i].next){
ll y=a[i].to;siz+=dfs(y);
T.Merge(T.Get(x),T.Get(y));
}
while(sum[T.Get(x)]>m)
T.Del(T.Get(x)),siz--;
ans=max(ans,siz*lead[x]);
return siz;
}
int main()
{
scanf("%lld%lld",&n,&m);
for(ll i=1;i<=n;i++){
ll x;
scanf("%lld%lld%lld",&x,&val[i],&lead[i]);
sum[i]=val[i];T.fa[i]=i;
if(x)addl(x,i);else k[i]=1;
}
for(ll i=1;i<=n;i++)
if(k[i]) dfs(i);
printf("%lld",ans);
}