AtCoder ABC324G 启发式合并

题意

传送门 AtCoder ABC324G Generate Arrays

题解

逆则操作顺序考虑,可以看作至多 n n n 个联通分量不断合并的过程,此时使用启发式合并,即规模较小的连通分量向规模较大的连通分量合并,以单个元素合并为基本运算,则基本运算次数为 O ( n log ⁡ n ) O(n\log n) O(nlogn)

使用 std::set 分别维护以索引和值为关键字的集合,每次分裂出较小的分量即可。关于如何计算出所分裂两个分量的规模的问题,对于以索引为关键字的平衡树,操作直接给出了右侧元素的相对位置 x i x_i xi,则规模可以直接计算;而对于以值为关键字的平衡树,操作仅给出了需要大于的值 x i x_i xi,此时平衡树无法直接计算相对位置,那么可以二分出第一个满足 v a l > x i val>x_i val>xi 的位置,使用两个迭代器分别不断左右移动直到边界,则在操作数限制为较小分量规模大小的约束下,计算出了分裂的两个分量的规模。总时间复杂度 O ( n log ⁡ 2 n ) O(n\log^2 n) O(nlog2n)

#include 
using namespace std;
using ll = long long;
int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    int n;
    cin >> n;
    vector<int> a(n);
    for (int i = 0; i < n; ++i) {
        cin >> a[i];
    }
    int q;
    cin >> q;
    using pst = set<pair<int, int>>;
    vector<pst> pos(q + 1), val(q + 1);
    for (int i = 0; i < n; ++i) {
        pos[0].insert({i, a[i]});
        val[0].insert({a[i], i});
    }
    for (int i = 1; i <= q; ++i) {
        int t, s, x;
        cin >> t >> s >> x;
        auto change = [](int a, int b, pst &_pos, pst &_val, pst &pos, pst &val) {
            if (a > b) {
                for (int j = 0; j < b; ++j) {
                    auto [k, a] = *prev(_pos.end());
                    _pos.erase({k, a});
                    _val.erase({a, k});
                    pos.insert({k, a});
                    val.insert({a, k});
                }
            } else {
                swap(_pos, pos);
                swap(_val, val);
                for (int j = 0; j < a; ++j) {
                    auto [k, a] = *pos.begin();
                    pos.erase({k, a});
                    val.erase({a, k});
                    _pos.insert({k, a});
                    _val.insert({a, k});
                }
            }
        };
        auto &_pos = pos[s];
        auto &_val = val[s];
        int a = -1, b = -1;
        if (t == 1) {
            int _n = _pos.size();
            if (x < _n) {
                a = x, b = _n - x;
            } else {
                a = _n, b = 0;
            }
            change(a, b, _pos, _val, pos[i], val[i]);
        } else {
            int _n = _val.size();
            auto it = _val.upper_bound({x, 1e9});
            if (it == _val.end()) {
                a = _n, b = 0;
            } else if (it == _val.begin()) {
                a = 0, b = _n;
            } else {
                a = b = 0;
                auto it2 = prev(it);
                for (;;) {
                    a += 1;
                    b += 1;
                    if (it2 == _val.begin()) {
                        b += _n - 2 * a;
                        break;
                    }
                    it2 = prev(it2);
                    it = next(it);
                    if (it == _val.end()) {
                        a += _n - 2 * a;
                        break;
                    }
                }
            }
            change(a, b, _val, _pos, val[i], pos[i]);
        }
        cout << (int)pos[i].size() << '\n';
    }

    return 0;
}

你可能感兴趣的:(数据结构,基本算法,算法)