BZOJ 2809 (APIO 2012) 左偏树
2015-08-18 15:01
155 查看
基本算得上左偏树的模板题。
(我的逗B)思路 : 想到每个忍者都可以被自己选为领导者,这样一来就只能选他的下属以及他服务客人,因为所给的树结构是以编号为关键字排序的,又因为选取一个领导者时,他的下属的顺序是不影响到最终客人的满意度的,所以我们dfs枚举每个人做领导者,以他们的花费为关键字写一个小根堆,(小根堆中的元素就是原本给出的树中被选做领导者的那颗子树),其中根节点是例外的,有可能不是最小值。由于是递归的,计算一个节点的最大满意度时首先要把他的所有子节点进行合并,又因为根节点是例外的,在每层递归结束时要维护小根堆性质,所以我们合并的并不是在原来的数里它的下属,所以这就又需要dfs函数返回一个值,代表它的“新下属”,一个小根堆的堆顶。注意最开始给定的树和我们写的左偏树是互不相干的。这样,每一次递归,我们需要计算的是一个最大满意度,求一个max,我们有的条件就是一个小根堆。因为我们枚举每个为根节点就在复杂度里乘了一个n,所以就要求在每次递归时,需要快速的从一个小根堆中求出最多可以选出多少忍者。由于枚举时是强制枚举对象为树根的,它可能不满足小根堆,所以会麻烦一点。 (这是我最开始想到的思路, 发现写到这里就会挂掉,于是灰心地去看了下PPT中的提示)
(可以A掉的)思路:仍然是dfs枚举每个人为领导者,与我的思路不同的是维护的是一个大根堆,多记录一些信息,是堆里的花费总和以及节点数。这样一来,每次枚举的过程中,只要花费总和大于了上限,就删除堆顶,而这个被删除的堆顶,在递归回溯的过程中是不会再被用到的,因为在回溯过程中,这棵树是被作为一颗子树使用的,而它自身已经达到花费上限了,所以肯定还要删除,继续删除堆顶。需要注意的是,dfs的过程是要返回树根的,因为比如dfs(x),而x在之后的过程中被删除或者不是最大,都不会成为堆顶,而合并的是堆顶,所以dfs需要返回堆顶编号,这样也就解决了我的逗B思路中的“小根堆的堆顶可能例外”的情况。
略兴奋,虽然借鉴了别人的思路和通用的模板(自己写的模板在上一篇博客中有的,后来给删了,因为它会挂掉),不过初次在BZOJ上交题一遍A,现在还有点小激动,下面是代码:
(我的逗B)思路 : 想到每个忍者都可以被自己选为领导者,这样一来就只能选他的下属以及他服务客人,因为所给的树结构是以编号为关键字排序的,又因为选取一个领导者时,他的下属的顺序是不影响到最终客人的满意度的,所以我们dfs枚举每个人做领导者,以他们的花费为关键字写一个小根堆,(小根堆中的元素就是原本给出的树中被选做领导者的那颗子树),其中根节点是例外的,有可能不是最小值。由于是递归的,计算一个节点的最大满意度时首先要把他的所有子节点进行合并,又因为根节点是例外的,在每层递归结束时要维护小根堆性质,所以我们合并的并不是在原来的数里它的下属,所以这就又需要dfs函数返回一个值,代表它的“新下属”,一个小根堆的堆顶。注意最开始给定的树和我们写的左偏树是互不相干的。这样,每一次递归,我们需要计算的是一个最大满意度,求一个max,我们有的条件就是一个小根堆。因为我们枚举每个为根节点就在复杂度里乘了一个n,所以就要求在每次递归时,需要快速的从一个小根堆中求出最多可以选出多少忍者。由于枚举时是强制枚举对象为树根的,它可能不满足小根堆,所以会麻烦一点。 (这是我最开始想到的思路, 发现写到这里就会挂掉,于是灰心地去看了下PPT中的提示)
(可以A掉的)思路:仍然是dfs枚举每个人为领导者,与我的思路不同的是维护的是一个大根堆,多记录一些信息,是堆里的花费总和以及节点数。这样一来,每次枚举的过程中,只要花费总和大于了上限,就删除堆顶,而这个被删除的堆顶,在递归回溯的过程中是不会再被用到的,因为在回溯过程中,这棵树是被作为一颗子树使用的,而它自身已经达到花费上限了,所以肯定还要删除,继续删除堆顶。需要注意的是,dfs的过程是要返回树根的,因为比如dfs(x),而x在之后的过程中被删除或者不是最大,都不会成为堆顶,而合并的是堆顶,所以dfs需要返回堆顶编号,这样也就解决了我的逗B思路中的“小根堆的堆顶可能例外”的情况。
略兴奋,虽然借鉴了别人的思路和通用的模板(自己写的模板在上一篇博客中有的,后来给删了,因为它会挂掉),不过初次在BZOJ上交题一遍A,现在还有点小激动,下面是代码:
#include <cstdio> #include <algorithm> #include <cstring> #include <vector> using namespace std; int n, m; unsigned long long ans; vector <int> sub[100005]; struct node{ int led, rew, lc, rc, h; unsigned long long sum, size; }t[100005]; void init(){ int mas, rew, led; scanf("%d %d", &n, &m); for(int i = 1; i <= n; i++){ scanf("%d %d %d", &mas, &rew, &led); t[i].rew = rew; t[i].led = led; t[i].size = 1; t[i].sum = rew; sub[mas].push_back(i); } } int merge(int A, int B){ if(!A) return B; if(!B) return A; if(t[A].rew < t[B].rew) swap(A, B); t[A].rc = merge(t[A].rc, B); int lc = t[A].lc, rc = t[A].rc; t[A].sum = t[lc].sum + t[rc].sum + t[A].rew; t[A].size = t[lc].size + t[rc].size + 1; if(t[t[A].lc].h < t[t[A].rc].h) swap(t[A].lc, t[A].rc); if(t[A].rc) t[A].h = t[A].rc+1; else t[A].h = 0; return A; } int dfs(int x){ int root = x; for(int i = 0; i < sub[x].size(); i++){ int c = sub[x][i]; root = merge(root, dfs(c)); } while(t[root].sum > m) root = merge(t[root].lc, t[root].rc); ans = max(ans, t[root].size * t[x].led); return root; } int main() { init(); dfs(1); printf("%lld", ans); return 0; }
相关文章推荐
- VMware11 + centOs 7 安装及网络配置
- 一些常见算法复杂度总结
- java工厂方法模式
- hdoj 3635 Dragon Balls
- 【effective c++读书笔记】【第8章】定制new和delete(1)
- 网页前台小知识
- JS禁止浏览器后退键
- 【effective c++读书笔记】【第8章】定制new和delete(1)
- a中调用js的几种方法
- mysql5.6删除的功能
- Android开发:学习笔记五十例(更新中)
- Centos 安装gitlab
- ".pyc"文件是什么文件
- smb协议
- lvs负载均衡集群之DR模式
- android变化HOLO对话风格
- 【并查集】POJ2236-Wireless Network
- json_encode处理中文乱码
- 机器学习数据源
- Linux中LVS-NAT模型解析