公式

E - Reverse 2^i 解説 by en_translator


Suppose we applied several operations to \(P\) and obtained a permutation \(Q\).

Then one of the following holds: \(\{P_0,P_1,\ldots,P_{2^{N-1}-1}\}=\{Q_0,Q_1,Q_2,\ldots,Q_{2^{N-1}-1}\}\) or \(\{P_0,P_1,\ldots,P_{2^{N-1}-1}\}=\{Q_{2^{N-1}},Q_{2^{N-1}+1},\ldots,Q_{2^N-1}\}\). This is true because any operation except for \((a,b)=(0,N)\) does not swap elements between \(P_0,P_1,\ldots,P_{2^{N-1}-1}\) and \(P_{2^{N-1}},P_{2^{N-1}+1},\ldots,P_{2^N-1}\), while \((a,b)=(0,N)\) entirely swaps them. Also, any reachable permutation \(Q\) can be obtained without performing an operation with \((a,b)=(0,N)\) except for the last operation.

Let \(A\) be the lexicographically smallest sequence that can be obtained by performing operations against \((P_0,P_1,\ldots,P_{2^{N-1}-1})\) without performing \((a,b)=(0,N)\) operation, and \(B\) be that for \((P_{2^{N-1}},P_{2^{N-1}+1},\ldots,P_{2^N-1})\).

If \(A_0 < B_0\)

The lexicographically smallest sequence is the concatenation of \(A\) and \(B\) in order, which is achieved by not performing \((a,b)=(0,N)\) operation.

If \(A_0 > B_0\)

One can obtain a sequence that is a concatenation of \(B\) and \(A\) in order in the following way: perform operations with \((a,b)=(0,N-1),(1,N-1)\), then \((a,b)=(0,N)\). This is the lexicographically smallest sequence obtainable.

Therefore, the lexicographically smallest sequence obtainable from the original sequence can be found by solving the same problem for the former and latter half of the sequence independently. Obviously, a size-\(1\) sequence does not need an operation. Thus, the problem can be solved recursively. The time complexity is \(\mathrm{O}(N 2^N)\).

Sample code (C++):

#include <bits/stdc++.h>
using namespace std;
int main() {
    int t;
    cin >> t;
    for (int test = 0; test < t; ++test) {
        int n;
        cin >> n;
        vector<int> p(1 << n);
        for (auto& e : p)
            cin >> e;
        auto solve = [&](auto&& solve, int l, int r) -> vector<int> {
            if (r - l == 1) return {p[l]};
            int mid = (l + r) / 2;
            vector<int> a = solve(solve, l, mid);
            vector<int> b = solve(solve, mid, r);
            if (a[0] > b[0]) swap(a, b);
            a.insert(a.end(), b.begin(), b.end());
            return a;
        };
        vector<int> ans = solve(solve, 0, 1 << n);
        for (int i = 0; i < (1 << n); ++i) {
            cout << ans[i] << " \n"[i == (1 << n) - 1];
        }
    }
}

投稿日時:
最終更新: