E - Reverse 2^i Editorial by m1une


問題文の条件を満たすすべての \((a, b)\) について \(b\) の降順に、 \(\min(P_{a \times 2^b}, \ldots , P_{(a + \frac12) \times 2^{b} - 1}) > \min(P_{(a + \frac12) \times 2^{b} }, \ldots P_{(a + 1) \times 2^{b} - 1})\) であるならば、かつそのときに限り \(P_{a \times 2^b} \ldots P_{(a + 1) \times 2^{b} - 1}\) を反転するという操作を行えばよいです。

\(P\) は順列なので上記の不等式において両辺が等しくなることはありません。また、上記の手順における任意の \((a, b)\) について、それ以降の操作を適切に行うことで \(P_{a \times 2^b}, \ldots , P_{(a + \frac12) \times 2^{b} - 1}\)\(P_{(a + \frac12) \times 2^{b} }, \ldots P_{(a + 1) \times 2^{b} - 1}\) それぞれの区間について最小値を区間の先頭に持ってくることができるので、これによって辞書順最小が達成できることがわかります。

実装例(C++)

#include <bits/stdc++.h>
using namespace std;

void solve() {
    int N;
    cin >> N;
    vector<int> P(1 << N);
    for (int i = 0; i < (1 << N); ++i) {
        cin >> P[i];
    }
    for (int b = N; b >= 1; --b) {
        for (int a = 0; a < (1 << (N - b)); ++a) {
            int l = a * (1 << b);
            int m = l + (1 << (b - 1));
            int r = l + (1 << b);
            int lmin = *min_element(P.begin() + l, P.begin() + m);
            int rmin = *min_element(P.begin() + m, P.begin() + r);
            if (lmin > rmin) {
                reverse(P.begin() + l, P.begin() + r);
            }
        }
    }
    for (int i = 0; i < (1 << N); ++i) {
        cout << P[i] << " \n"[i == (1 << N) - 1];
    }
}

int main() {
    int T;
    cin >> T;
    while (T--) solve();
}

posted:
last update: