题目链接:右转进入题目
题目大意:自行参考原题
题解:
算法是不难想到的,主体是dfs一遍,对于第i个点为管理者的情况,先处理出以第i个点为根的子树中所有点为管理者的答案;(假设已经处理好了)
那么要怎么做呢?显然,为了不超过预算,我们要把i这个人和i的子树中的人放到一块去,然后排个序,贪心的选取能力值较小的,能选多少是多少
设选了sz个,那么以第i个人为管理者的答案就是sz*L[i]。
但这样还是不好处理。
这里用到了黄学长那篇里面BOI的sequence辣道题的一个思想:如何求中位数呢?很简单,把小于中位数的数字全部扔掉,那么堆首不就是中位数了么?
同理,我们注意到,如果一个点,在以i为管理者时不被派遣,那么在以father[i],father[father[i]]....为管理者时也不会被派遣。
更进一步的说,我们只想要一个有序数列中,前面的连续项,使得这些项的和不超过M。然后想知道这连续项到底有多少项。
换句话说,对于一个非降序列,我们从队尾一直delete,到数列中的和<=M即可,这时数列的长度就是sz。
被删除的数到之后的计算中也一定不会被选中,因此删除之后也不需要考虑什么啦!
有些时候我们处理以i个根的子树,要把结点i全部的son的序列合并成一个序列。
因此,要维护这样一个数据结构,它支持:
1,查询一个数列的最大值
2,删除一个数列的最大值
3,合并两个数列
4,询问数列中元素个数
哈!辣不就是可并堆嘛!
更准确的说,这里使用左偏树,它的合并、删除操作时O(lgn)的,查询时O(1)的。
左偏树是这样一个东西,它满足堆性质,即,每一个结点的值都要比他的子树中所有点的值要小(或大),由堆性质可知根节点就是最小值/最大值。
同时他还满足一个叫做左偏性质的东西。对于每个点rt,我们维护一个dist,满足:
当这个点没有right儿子时候,rt.dist=0,否则rt.dist=rt.right_child.dist+1。
这是个什么东东我也是一知半解%%%……
先说最重要的合并操作,伪代码描述如下(算法描述见黄学长《左偏树的特点及其应用》):
leftist *merge(leftist *A,leftist *B)//¼ÙÉèÊÇС¸ù¶Ñ
{
if(A==NULL) return B;
if(B==NULL) return A;
if(A->value>B->value) swap(A,B);
A->right=merge(A->right,B);
if(A->right->dist>A->left->right)
swap(A->right,A->left);
if(A->right==NULL) A->dist=0;
else A->dist=A->right->dist+1;
// push_up(A);
return A;
}
int merge(int x,int y)
{
if(!x||!y) return x+y;
if(value[x]>value[y]) swap(x,y);
right[x]=merge(right[x],y);
if(dist[right[x]]>dist[left[x]])
swap(right[x],left[x]);
if(right[x]) dist[x]=dist[right[x]]+1;
else dist[x]=0;
return x;
}
void pop(leftist* &A)
{
A=merge(A->right,A->left);
}
void pop(int &x)
{
x=merge(right[x],left[x]);
}
这样的话,描述一下之前辣个算法:
dfs。如果当前这个点x是叶子结点,非常好处理对吧
如果不是,记root[x]为x这个点所在的堆的堆顶编号,初始化root[x]=x。
把所有root[son[x]]都合并起来再和root[x]合并(其实就是把x插入),如果当前堆的所有元素的和>M,就pop掉最大值,一直删除到sum<=M为止。
此时堆中元素个数sz[x]*L[x]就是以x为领导的答案。
最后MAX{sz[x]*L[x]}就是答案
代码如下:
#include
#include
#include
#include
#define ull long long
#define MAXN 100010
using namespace std;
ull m,val[MAXN],sumn[MAXN],L[MAXN],ans,sz[MAXN];
int n,fa[MAXN],rig[MAXN],lef[MAXN],dist[MAXN],root[MAXN];
vector g[MAXN];
int merge_lst(int x,int y)
{
if(!x||!y) return x+y;
if(val[x]=0;i--)
if(g[rt][i]!=fa[rt])
{
dfs(g[rt][i]);
root[rt]=merge_lst(root[rt],root[g[rt][i]]);
while(getsum(root[rt])>m) pop(root[rt]);
}
ans=max(ans,L[rt]*getsz(root[rt]));
return 0;
}
int main()
{
scanf("%d%lld",&n,&m);
int rt;
for(int i=1;i<=n;i++)
{
scanf("%d%lld%lld",&fa[i],&val[i],&L[i]);
if(fa[i]==0) rt=i;sz[i]=1;
g[fa[i]].push_back(i);
dist[i]=lef[i]=rig[i]=0;
root[i]=i;sumn[i]=val[i];
}
ans=-1;dfs(rt);
printf("%lld\n",ans);return 0;
}