E - Sorted and Sorted | AtCoder Regular Contest 097

隣接要素同士の交換しかできないのだから、もし白または黒のみであれば、最小の操作回数は転倒数に等しい。

この問題では、白と黒の2色の並び方ごとに操作回数が異なる。しかし、白と黒の並びが定まれば、操作回数は一意に定まる。なぜなら、各並び方について、白黒区別なく先頭から1~2Nと番号をつけ直せば、ただの順列のソートであるため。

先頭の要素からソートしていく。

dp[w][b]:=「先頭へ詰めて白1~w, 黒1~bをソート済みのときの最小の操作回数」

とおく。先頭からw+b個はソート済みということ。 このとき、次に白、黒のどちらがきても dp[w+1][b]またはdp[w][b+1]のどちらかを更新することで対応できる。dp[N][N]が解。

状態(w, b)で次に白または黒をw+b+1番目まで持ってくるときのコストを知る必要がある。対称性から、次に白が来る場合だけを考える(黒の場合も同様)。w=b=0のとき、白1番がk番目にあるとすると、コストk-1で先頭まで持って来れる。状態から(w, b)の場合は、kより左にあってソート済みの要素を超えて左に行く必要がないのでコストはkより左にあってソート済みでない要素の個数。左にあってソート済みでない要素の個数はBinary Indexed TreeでDPのループごとに計算できる。

template<class Val>
struct BinaryIndexedTree {
    int n;
    vector<Val> t;
    BinaryIndexedTree() {}
    BinaryIndexedTree(int _n) :n(_n + 1), t(_n + 1) {}
    void add(int k, Val val) {
        k++;
        while (k < n) {
            t[k] += val;
            k += (k&-k);
        }
    }
    void set(int k, Val val) {
        add(k, -sum(k, k + 1));
        add(k, val);
    }
    Val sum(int k) {
        Val r = 0;
        while (k > 0) {
            r += t[k];
            k -= (k&-k);
        }
        return r;
    }
    Val sum(int l, int r) {
        return sum(r) - sum(l);
    }
};
typedef BinaryIndexedTree<int> BIT;

const ll INF = 1ll << 60;
ll dp[2001][2001];

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout << fixed << setprecision(20);

    int N;
    cin >> N;
    vector<char> C(2*N);
    vi A(2*N), wk(N), bk(N);
    rep(i, 2*N) {
        cin >> C[i] >> A[i];
        --A[i];
        if (C[i] == 'W')wk[A[i]] = i;
        else bk[A[i]] = i;
    }

    rep(i, N + 1)rep(j, N + 1)dp[i][j] = INF;
    dp[0][0] = 0;
    BIT bit(2 * N);
    rep(i, 2 * N)bit.add(i, 1);

    rep(w, N+1) {
        rep(b, N+1) {
            if (w < N) {
                ll x = bit.sum(wk[w]);
                smin(dp[w + 1][b], dp[w][b] + x);
            }
            if (b < N) {
                ll x = bit.sum(bk[b]);
                smin(dp[w][b + 1], dp[w][b] + x);
            }
            if (b < N)bit.add(bk[b], -1);
        }
        rep(b, N)bit.add(bk[b], 1);
        if(w<N)bit.add(wk[w], -1);
    }

    cout << dp[N][N] << endl;
}