Codeforces 1329C - Drazil Likes Heap(堆+贪心)

栏目: IT技术 · 发布时间: 5年前

内容简介:给出一个高度为 h 的大根堆, 要求弹出其中若干个数后高度变为 g, 并且前后大根堆都是满二叉树. 问新的大根堆所有数之和的最小值, 并要给出一种弹出数的操作序列(节点序号). h, g ≤ 20. 堆的弹出过程见如下代码:

题目链接

题意

给出一个高度为 h 的大根堆, 要求弹出其中若干个数后高度变为 g, 并且前后大根堆都是满二叉树. 问新的大根堆所有数之和的最小值, 并要给出一种弹出数的操作序列(节点序号). h, g ≤ 20. 堆的弹出过程见如下代码:

Codeforces 1329C - Drazil Likes Heap(堆+贪心)

题解

考虑新堆的每个节点的可能最小结果: 如果是叶子节点, 那么最小是取它的子树中最小的数; 否则, 取子树中比两个儿子大的数中的最小值.

那么, 能否能得到这个每个节点都取到最小值的堆呢, 回答是肯定的. 我们在得知哪些数字会留下后, 是要把其他点弹出的. 弹出这些数后, 我们还需要考虑新堆是不是满二叉树. 按序号从小到大考虑每个节点, 会发现(这个过程不太好描述, 主要还是堆的性质)每个节点最后的结果一定是以它为根的子树中剩余数字的最大值(被祖先节点使用的话是要移除的), 既然每个节点都有数字, 这个堆自然是满二叉堆.

要得到新堆的每个节点的数字, 可以从下到上维护每个节点的子树的序列. 每次合并都是 O(len) (len为序列长度) 的, 总的时间复杂度为 O(nlogn). 要输出操作序列时, 可以从下往上弹出不需要的数字, 这样不会影响到祖先的结构.

代码

#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define inc(i, l, r) for (int i = l; i <= r; i++)
#define dec(i, l, r) for (int i = l; i >= r; i--)

const int maxn = (1 << 20) + 5;

int t, h, g;
int val[maxn], nxt[maxn];
vector<int> sub[maxn];

inline vector<int> Merge(vector<int> v1, vector<int> v2, int id) {
    int sz1 = v1.size(), sz2 = v2.size();
    vector<int> r(sz1 + sz2 + 1);
    for (int i = 0, j = 0; i + j < sz1 + sz2;) {
        if (j == sz2 || i < sz1 && v1[i] < v2[j]) {
            r[i + j] = v1[i];
            i++;
        } else {
            r[i + j] = v2[j];
            j++;
        }
    }
    r[sz1 + sz2] = val[id];
    return r;
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    cin >> t;
    while (t--) {
        cin >> h >> g;
        inc(i, 1, (1 << h) - 1) cin >> val[i];
        inc(i, 1 << h - 1, (1 << h) - 1) sub[i] = vector<int>(1, val[i]);
        dec(i, (1 << h - 1) - 1, 1) {
            sub[i] = Merge(sub[i << 1], sub[i << 1 | 1], i);
        }
        set<int> s;
        ll sum = 0;
        inc(i, 1 << g - 1, (1 << g) - 1) {
            nxt[i] = sub[i][0];
            s.insert(nxt[i]);
            sum += nxt[i];
        }
        dec(i, (1 << g - 1) - 1, 1) {
            inc(j, 0, (int)sub[i].size() - 1) if (sub[i][j] > nxt[i << 1] &&
                                                  sub[i][j] > nxt[i << 1 | 1]) {
                nxt[i] = sub[i][j];
                s.insert(nxt[i]);
                sum += nxt[i];
                break;
            }
        }
        cout << sum << "\n";
        dec(i, (1 << h) - 1, 1) {
            if (s.find(val[i]) == s.end()) cout << i << " ";
        }
        cout << "\n";
    }
}

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Data Structures and Algorithm Analysis in Java

Data Structures and Algorithm Analysis in Java

Mark A. Weiss / Pearson / 2011-11-18 / GBP 129.99

Data Structures and Algorithm Analysis in Java is an “advanced algorithms” book that fits between traditional CS2 and Algorithms Analysis courses. In the old ACM Curriculum Guidelines, this course wa......一起来看看 《Data Structures and Algorithm Analysis in Java》 这本书的介绍吧!

JSON 在线解析
JSON 在线解析

在线 JSON 格式化工具

MD5 加密
MD5 加密

MD5 加密工具

HEX HSV 转换工具
HEX HSV 转换工具

HEX HSV 互换工具