题目描述:
在树中找到一个点i,并且找到这个点子树中的一些点组成一个集合,使得集合中的所有点的c之和不超过M,且Li*集合中元素个数和最大。
题目分析:
首先贪心的想一下。
我们把i的子树及自己的元素全部排一下序,然后累加,去找最多的元素,sumc<=m
那么答案即为 Li*元素个数
那我们就可以枚举所有的点,每次都做一下这个过程,取max
如何快速从子树中得到信息呢?
对于这个排序,我们可以用可合并堆来搞.
每次把当前点堆与儿子堆合并,维护大根堆,然后pop最大元素,直到sumc<=m
这时ans=li*siz堆
堆合并logn
总的复杂度就是个 nlogn
注意开long long -_-
题目链接:
Ac 代码:
#include <iostream>
#include <cstdio>
#define int long long
const int maxm=110000;
struct left_tree{
int ls,rs;
int dis,v;
int sum,siz;
}st[maxm];
int rt[maxm],l[maxm],maxp,ans,n;
int head[maxm],to[maxm],net[maxm],cnt;
inline void addedge(int u,int v)
{
cnt++;
to[cnt]=v,net[cnt]=head[u],head[u]=cnt;
}
inline int merge(int x,int y)
{
if(!x||!y) return x+y;
if(st[x].v<st[y].v) std::swap(x,y);
st[x].rs=merge(st[x].rs,y);
if(st[st[x].ls].dis<st[st[x].rs].dis) std::swap(st[x].ls,st[x].rs);
st[x].dis=st[st[x].rs].dis+1;
st[x].sum=st[st[x].ls].sum+st[st[x].rs].sum+st[x].v;
st[x].siz=st[st[x].ls].siz+st[st[x].rs].siz+1;
return x;
}
inline int pop(int x)
{
return merge(st[x].ls,st[x].rs);
}
void dfs(int now,int fa)
{
for(int i=head[now];i;i=net[i])
if(to[i]!=fa)
dfs(to[i],now);
for(int i=head[now];i;i=net[i])
if(to[i]!=fa)
rt[now]=merge(rt[now],rt[to[i]]);
//printf("-%d %d %d-\n",now,st[rt[now]].v,st[rt[now]].sum);
while(rt[now]&&st[rt[now]].sum>maxp)
rt[now]=pop(rt[now]);
ans=std::max(ans,l[now]*(st[rt[now]].siz));
}
main()
{
scanf("%lld%lld",&n,&maxp);
int root;
for(int i=1;i<=n;i++)
{
int fa;
scanf("%lld%lld%lld",&fa,&st[i].v,&l[i]);
if(!fa) root=i;
else addedge(fa,i);
st[i].siz=1,st[i].sum=st[i].v;
rt[i]=merge(rt[i],i);
}
dfs(root,0);
printf("%lld\n",ans);
return 0;
}