正题
题目链接:https://www.luogu.com.cn/problem/P1552
题目大意
一个nnn个点森林,每个点有价值和代价,选择一个点并在这个点的子树中选择一些点使得。
选择的点数∗该点的价值选择的点数*该点的价值选择的点数∗该点的价值最大且选择的点的代价之和不超过mmm。
解题思路
我们对于每个点,我们将所有子节点处理完后将子节点的左偏树合并,然后不停弹出代价最大的点知道代价之和小于等于mmm即可。
时间复杂度O(nlogn)O(n\log n)O(nlogn)
codecodecode
#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);
}