公式

D - Swap to Gather 解説 by yuto1115

解説

いくつかの解法が考えられますが、ここでは実装が最も簡単である(と思われる)ものを解説します。少々形式的な説明が多くなるため、最初に直観的な説明を載せておきます。

直観的な説明 $S$ に含まれる各 0 について、それより左にある 1 の個数($l$ とおく)とそれより右にある 1 の個数($r$ とおく)を考える。$l< r$ のとき、その 01 のかたまりの左側に持ってくる方がお得。逆に $l\geq r$ のとき、その 01 のかたまりの右側に持ってくる方がお得。よって、答えは $\min\{l,r\}$ をすべての 0 について足し合わせたものとなる。

\(S\) に含まれる 0 の個数を \(C_0\)1 の個数を \(C_1\ (=N-C_0)\) とおきます。

\(i\ (1\leq i\leq C_0)\) について、\(d_i\) を「左から \(i\) 番目の 0 よりも左にある 1 の個数」と定義します。また、便宜上 \(d_0=0, d_{C_0+1}=C_1\) と定義します。このとき、\(d_0 \leq d_1\leq d_2\leq \dots \leq d_{C_0}\leq d_{C_0+1}\) です。

\(d_i\) の値に着目して本問題を捉え直してみましょう。まず、同じ文字同士を入れ替える操作には意味がないことから、元の問題における操作は、\(d_i\) に対して以下のいずれかの操作を行うことと同値です。

  • \(d_{i-1}<d_i\) なる \(i\ (1\leq i\leq C_0)\) を一つ選び、\(d_i\)\(1\) 減少させる。
  • \(d_i<d_{i+1}\) なる \(i\ (1\leq i\leq C_0)\) を一つ選び、\(d_i\)\(1\) 増加させる。

また、終了条件は以下と同値です。

  • ある \(p\ (0\leq p\leq C_0)\) が存在し、\(0=d_1=\dots=d_p\) かつ \(d_{p+1}=\dots=d_{C_0}=C_1\)

一回の操作では \(1\) つの \(d_i\) の値が \(1\) だけ変化することから、

\[K=\displaystyle\sum_{i=1}^{C_0} \min\{d_i, C_1-d_i\}\]

と定義すると、\(K\) 回以上の操作が明らかに必要です。

逆に、\(d_i\) が単調増加であることから、\(K\) 回の操作で終了条件を満たすことが常に可能なため、答えは \(K\) です。(\(d_i < C_1-d_i\) なる最大の \(i\) (存在しなければ \(0\)) を \(p\) とすればよいです。)

実装例 (C++) :

#include <bits/stdc++.h>

using namespace std;

int main() {
    int n;
    string s;
    cin >> n >> s;
    int c1 = 0;
    for (char c: s) {
        if (c == '1') ++c1;
    }
    int now = 0;
    long long ans = 0;
    for (char c: s) {
        if (c == '0') {
            ans += min(now, c1 - now);
        } else {
            ++now;
        }
    }
    cout << ans << endl;
}

投稿日時:
最終更新: