您的位置:首页 > 其它

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,现在还有点小激动,下面是代码:

#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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: