AtCoder Regular Contest 080 E - Young Maids

E - Young Maids

問題

長さ $N$ の順列 $(p_i)$ がある。これから以下の操作を $(p_i)$ が空になるまで繰り返して、順列 $(q_i)$ を作る。最初 $(q_i)$ は空である。

  • $(p_i)$ の隣接する 2 要素を選ぶ(順に $x, y$ とする)。
  • $x,y$ を $(p_i)$ から削除し、 $x, y$ をこの順に $(q_i)$ の 先頭 に挿入する。

$(q_i)$ として考えられるもののうち、辞書順最小のものを求めよ。

制約

  • $2 \leq N \leq 2 \cdot 10^5$
  • $N$ は偶数

考察

辞書順最小なので、先頭の要素が決まる最後の操作から考える。

最後に選んだ要素が $p_i, p_j$ だったとする。 このとき、それより前に選ばれたペアは $p_i, p_j$ をどちらも跨いではいけない。 つまり、操作後に数列が $[0, i), [i+1, j), [j+1, N)$ の 3 つの区間に分割される、と考えられる。

各区間は長さが偶数でないと数列が作れない。 よって $i, j$ がそれぞれ(0-indexed で)偶数、奇数でないといけない。 以上より、

  • $i$ が偶数
  • $j$ が奇数
  • $i \lt j$

を満たす $i, j$ のうち、 $p_i, p_j$ が辞書順最小であるものを選ぶのが最善となる。 このような $p_i, p_j$ は偶数、奇数番目のみをまとめた最小値取得セグメント木で求められる。最小値の index が欲しくなるが、これは値と一緒に index を持たせればよい。

その後は区間 $[0, i), [i+1, j), [j+1, N)$ から 1 つを選び、その中のペアを同様に選ぶ。 これも貪欲に選びたいので、区間 $[l, r)$ を選べる最小の $x$ と一緒に heap で管理すれば、常に最善の区間を選べる。

実装例

提出 #18402693 - AtCoder Regular Contest 080

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
#include <atcoder/segtree>
#include <iostream>
#include <vector>
#include <queue>

template <class T>
using MinHeap = std::priority_queue<T, std::vector<T>, std::greater<T>>;

using namespace std;

struct Data {
    int val, idx;
    explicit Data(int val, int idx) : val(val), idx(idx) {}

    static Data op(Data a, Data b) { return a.val <= b.val ? a : b; }
    static Data e() { return Data(1 << 30, -1); }
};

void solve() {
    int n;
    cin >> n;

    atcoder::segtree<Data, Data::op, Data::e> eseg(n), oseg(n);
    for (int i = 0; i < n; ++i) {
        int p;
        cin >> p;
        (i % 2 == 0 ? eseg : oseg).set(i, Data(p, i));
    }

    vector<int> ans;

    MinHeap<tuple<int, int, int>> heap;
    // val, [l, r)

    // 区間[l, r)を(空でなければ)最小値と共に追加
    auto push = [&](int l, int r) {
        if (r <= l) return;
        auto d = (l % 2 == 0 ? eseg : oseg).prod(l, r);
        heap.emplace(d.val, l, r);
    };

    push(0, n);
    while (!heap.empty()) {
        auto [v, l, r] = heap.top();
        heap.pop();

        auto x = (l % 2 == 0 ? eseg : oseg).prod(l, r);
        auto lr = x.idx;
        ans.push_back(x.val);

        auto y = (l % 2 == 0 ? oseg : eseg).prod(lr, r);
        auto rl = y.idx;
        ans.push_back(y.val);

        push(l, lr);
        push(lr + 1, rl);
        push(rl + 1, r);
    }

    for (auto p : ans) cout << p << " ";
    cout << "\n";
}
Built with Hugo
テーマ StackJimmy によって設計されています。