BZOJ2809 [APIO2012]dispatching-左偏树-左偏树学习笔记

来源:互联网 发布:xbox360体感游戏知乎 编辑:程序博客网 时间:2024/06/05 12:41

题目链接:右转进入题目

题目大意:自行参考原题

题解:

算法是不难想到的,主体是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);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<iostream>#include<cstdio>#include<vector>#include<algorithm>#define ull long long#define MAXN 100010using 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<int> g[MAXN];int merge_lst(int x,int y){    if(!x||!y) return x+y;    if(val[x]<val[y]) swap(x,y);    rig[x]=merge_lst(rig[x],y);    sumn[x]=sumn[lef[x]]+sumn[rig[x]]+val[x];    sz[x]=sz[rig[x]]+sz[lef[x]]+1;    if(dist[lef[x]]<dist[rig[x]])        swap(lef[x],rig[x]);    dist[x]=dist[rig[x]]+1;    return x;}inline ull getsum(int x){    return sumn[x];}inline int getsz(int x){    return sz[x];}void pop(int &x){    x=merge_lst(rig[x],lef[x]);return;}int dfs(int rt){    for(int i=g[rt].size()-1;i>=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;}




0 0
原创粉丝点击