Official

D - Swap to Gather Editorial by en_translator


Among several possible solutions, here we will introduce the one that is (presumably) easiest to implement. Before going into long mathematical explanation, here is an intuitive understanding:

Intuitive explanation For each 0 in $S$, consider the number of 1 to its left (let it be $l$) and the number of 1 to its right (let it be $r$). If $l< r$, it's better to bring 0 to the left of the 1 chunk. If $l< r$, it's better to bring 0 to the left of the 1 chunk. Thus, the answer is the sum of $\min\{l,r\}$ for all 0.

Let \(C_0\) be the number of 0 in \(S\), and \(C_1\) be the number of 1 \((=N-C_0)\).

For each \(i\ (1\leq i\leq C_0)\), let \(d_i\) be the number of 1s to the right of the \(i\)-th 0 from the left. For convenience, define \(d_0=0\) and \(d_{C_0+1}=C_1\). Then \(d_0 \leq d_1\leq d_2\leq \dots \leq d_{C_0}\leq d_{C_0+1}\).

Let us reinterpret the problem according to the value \(d_i\). First, swapping the same character is meaningless, so the operation in the original problem statement is equivalent to performing one of the following operations against \(d_i\):

  • Choose one \(i\ (1\leq i\leq C_0)\) with \(d_{i-1}<d_i\), and decrease \(d_i\) by \(1\).
  • Choose one \(i\ (1\leq i\leq C_0)\) with \(d_i<d_{i+1}\), and increase \(d_i\) by \(1\).

The termination condition is as follows:

  • There exists \(p\ (0\leq p\leq C_0)\) such that \(0=d_1=\dots=d_p\) and \(d_{p+1}=\dots=d_{C_0}=C_1\).

One operation changes \(d_i\) by one, so clearly at least \(K\) operations is required, where

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

On the other hand, since \(d_i\) is monotonically increasing, it is always possible to satisfy the termination condition with \(K\) operations, so the answer is \(K\). (One can let \(p\) be the largest \(i\) with \(d_i < C_1-d_i\), or \(0\) if it does not exist.)

Sample code (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;
}

posted:
last update: