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;}
- BZOJ2809 [APIO2012]dispatching-左偏树-左偏树学习笔记
- Bzoj2809:[Apio2012]dispatching:左偏树
- [APIO2012]bzoj2809 dispatching 左偏树
- BZOJ2809(Apio2012)[dispatching]--左偏树
- bzoj2809 [Apio2012]dispatching(左偏树)
- 【APIO2012】【BZOJ2809】派遣dispatching
- bzoj2809: [Apio2012]dispatching
- bzoj2809: [Apio2012]dispatching
- bzoj2809: [Apio2012]dispatching
- bzoj2809【APIO2012】dispatching
- 【bzoj2809】 Apio2012—dispatching
- 【APIO2012】bzoj2809 dispatching
- 【bzoj2809】[Apio2012]dispatching
- bzoj2809 [Apio2012]dispatching
- BZOJ2809: [Apio2012]dispatching
- [BZOJ2809] [Apio2012]dispatching
- bzoj2809 [Apio2012]dispatching
- bzoj2809 [Apio2012]dispatching
- Reverse a singly linked list python
- Protocol(协议)
- Vijos1790 拓扑编号 拓扑排序
- 创建学校图书馆数据库 BookDB
- 连号区间数
- BZOJ2809 [APIO2012]dispatching-左偏树-左偏树学习笔记
- Ecshop模板开发(六):商品详情页收藏商品实现
- 可视化的排序六:桶排序
- 计算机专外Week4-Exercises
- Quartz2D
- 插入排序
- xml 文件瘦身
- Linux cups 打印总结备忘
- 计蒜客蓝桥杯模拟赛 九宫格