Official

G - Cut and Reorder Editorial by en_translator


It is unnecessary to perform the former operation multiple times; one can assume that the former is performed only once in the beginning, then the latter.

If we consider the process of determining the sequence \((A _ {p _ 1},A _ {p _ 2},\ldots,A _ {p _ N})\) resulting from the former operation from its initial element, we come up with the following DP (Dynamic Programming):

  • \(\operatorname{dp}[S]\coloneqq\) the minimum cost required to determine the first \(|S|\) terms so that \(S=\lbrace p _ 1,p _ 2,\ldots,p _ {|S|}\rbrace\).

Here, the “cost” above is defined as the minimum cost that conforming initial \(|S|\) terms contribute; formally:

\[\min _ {\substack{p\text{ is a permutation of }(1,2,\ldots,N)\\\lbrace p _ 1,p _ 2,\ldots,p _ {|S|}\rbrace=S}}\left(C\times\#\lbrace i|1\leq i\lt|S|,p _ i+1\neq p _ {i+1}\rbrace+\sum _ {i=1} ^ {|S|}\left|A _ {p _ i}-B _ i\right|\right).\]

We consider transitions that correspond to a block formed by the former operation.

For each \(S\subset\lbrace1,2,\ldots,N\rbrace\), we perform a transition \(S\rightarrow S\cup\lbrace l,l+1,\ldots,r\rbrace\) for every pair \((l,r)\) such that \(S\cap\lbrack l,r\rbrack=\emptyset\ (1\leq l\leq r\leq N)\).

For each length-\(k\) segment, there are only \(2 ^ {N-k}\) sets \(S\) against which the segment applies a transition; therefore, the total number of transitions is \(\displaystyle\sum _ {k=1} ^ N2 ^ {N-k}(N+1-k)=2 ^ N(N-3)+2=O(2 ^ NN)\).

(Under the assumption that the word size \(w\) satisfies \(N\leq w\),) one can represent a subset of \(\lbrace1,2,\ldots,N\rbrace\) by a single-word integer, so that the overall complexity is \(O(2 ^ NN)\).

The following is sample code.

#include <iostream>
#include <vector>
#include <limits>
#include <bit>

int main() {
    using namespace std;

    unsigned N;
    unsigned long C;
    cin >> N >> C;
    vector<unsigned long> A(N), B(N);
    for (auto &&a : A)
        cin >> a;
    for (auto &&b : B)
        cin >> b;

    vector<unsigned long> dp(1U << N, numeric_limits<unsigned long>::max() / 2);
    dp[0] = -C; // はじめの 1 ブロックは分割のコストがかからないので -C しておく

    const auto chmin{[](auto &&x, const auto &y) {
        if (x > y)x = y;
        return x;
    }};

    for (unsigned long S{}; S < 1U << N; ++S)
        for (unsigned long B_front(popcount(S)), l{}; l < N; ++l)
            for (unsigned long cost{C}, r{l}, i{B_front}; r < N; ++r, ++i) {
                if ((S >> r) & 1) // 区間が重なったら終了
                    break;
                cost += max(A[r], B[i]) - min(A[r], B[i]); // 区間のコストに |A[p[i]] - B[i]| を加える
                chmin(dp[S | (1UL << r + 1) - (1UL << l)], dp[S] + cost); // コストを更新
            }

    cout << dp.back() << endl;
    return 0;
}

posted:
last update: