D. Binary String Sorting

D. Binary String Sorting

You are given a binary string $s$ consisting of only characters 0 and/or 1.

You can perform several operations on this string (possibly zero). There are two types of operations:

  • choose two consecutive elements and swap them. In order to perform this operation, you pay $10^{12}$ coins;
  • choose any element from the string and remove it. In order to perform this operation, you pay $10^{12}+1$ coins.

Your task is to calculate the minimum number of coins required to sort the string $s$ in non-decreasing order (i. e. transform $s$ so that $s_1 \le s_2 \le \dots \le s_m$, where $m$ is the length of the string after applying all operations). An empty string is also considered sorted in non-decreasing order.

Input

The first line contains a single integer $t$ ($1 \le t \le 10^4$) — the number of test cases.

The only line of each test case contains the string $s$ ($1 \le |s| \le 3 \cdot 10^5$), consisting of only characters 0 and/or 1.

The sum of lengths of all given strings doesn't exceed $3 \cdot 10^5$.

Output

For each test case, print a single integer — the minimum number of coins required to sort the string $s$ in non-decreasing order.

Example

input

6
100
0
0101
00101101
1001101
11111

output

1000000000001
0
1000000000000
2000000000001
2000000000002
0

Note

In the first example, you have to remove the $1$-st element, so the string becomes equal to 00.

In the second example, the string is already sorted.

In the third example, you have to swap the $2$-nd and the $3$-rd elements, so the string becomes equal to 0011.

In the fourth example, you have to swap the $3$-rd and the $4$-th elements, so the string becomes equal to 00011101, and then remove the $7$-th element, so the string becomes equal to 0001111.

In the fifth example, you have to remove the $1$-st element, so the string becomes equal to 001101, and then remove the $5$-th element, so the string becomes equal to 00111.

In the sixth example, the string is already sorted.

 

解题思路

  先给出dp的做法。

  定义状态

  1. $f(i,0)$表示将前$i$个数字处理成非递减序列,且最后一个数字是$0$的所有操作方案的集合。
  2. $f(i,1)$表示将前$i$个数字处理成非递减序列,且最后恰好有一个$1$的所有操作方案的集合。
  3. $f(i,2)$表示将前$i$个数字处理成非递减序列,且最后至少有两个连续的$1$的所有操作方案的集合。

  三种状态的属性均是是操作代价的最小值。

  每个状态$f(i,j), \ j \in \{0,1,2\}$根据前一次操作后的状态$f(i-1,k), \ k \in \{0,1,2\}$来转移得到。定义$M = {10}^{12}$。

  • 如果$s_i = 0$,
  1. 对于$f(i,0)$,可以直接从$f(i-1,0)$转移过来,代价就是$f(i-1,0)$。也可以从状态$f(i-1,1)$和$f(i-1,2)$删除最后所有的$1$转移到$f(i-1,0)$,最后再接上$s_i$转移过来,代价至少为$f(i-1,0)+M+1$。
  2. 对于对于$f(i,1)$,无法从状态$f(i-1,0)$转移过来。可以删除$s_i$然后直接从状态$f(i-1,1)$转移过来,代价是$f(i-1,1)+M+1$,或者$s_i$与状态$f(i-1,1)$最后一个$1$交换转移过来,代价是$f(i-1,1)+M$,很明显后者更小。也可以从状态$f(i-1,2)$删除若干个$1$得到$f(i-1,1)$,然后再删除$s_i$转移过来,代价至少为$f(i-1,1) + 2 \cdot (M+1)$。
  3. 对于对于$f(i,2)$,无法从状态$f(i-1,0)$和$f(i-1,1)$转移过来。可以删除$s_i$然后直接从状态$f(i-1,2)$转移过来,代价就是$f(i-1,2)+M+1$。
  • 如果$s_i = 1$,
  1. 对于$f(i,0)$,可以删除$s_i$然后直接从状态$f(i-1,0)$转移过来,代价是$f(i-1,0)+M+1$。也可以从状态$f(i-1,1)$和$f(i-1,2)$删除最后所有的$1$转移到$f(i-1,0)$,然后再删除$s_i$转移过来,代价至少为$f(i-1,0) + 2 \cdot (M+1)$。
  2. 对于对于$f(i,1)$,可以直接从$f(i-1,0)$转移过来,代价就是$f(i-1,0)$。也可以从状态$f(i-1,1)$和$f(i-1,2)$删除最后若干个$1$转移到$f(i-1,1)$,然后再删除$s_i$转移过来,代价至少为$f(i-1,1) + 2 \cdot (M+1)$。
  3. 对于对于$f(i,2)$,无法从状态$f(i-1,0)$转移过来。可以直接从状态$f(i-1,1)$和$f(i-1,2)$转移过来,代价就是$\min \{ f(i-1,1), f(i-1,2) \}$。

  综上所述,状态转移方程为:

\begin{align*}
&{s_i = 0: \begin{cases} f(i,0) = f(i-1,0) \\ f(i,1) = f(i-1,1) + M \\ f(i,2) = f(i-1,2) + M + 1 \end{cases}} \\\\
&{s_i = 1: \begin{cases} f(i,0) = f(i-1,0) + M + 1 \\ f(i,1) = f(i-1,0) \\ f(i,2) = \min\{f(i-1,1), \ f(i-1,2)\} \end{cases}}
\end{align*}

  AC代码如下:

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 
 4 typedef long long LL;
 5 
 6 const int N = 3e5 + 10;
 7 const LL M = 1e12;
 8 
 9 char s[N];
10 LL f[N][3];
11 
12 void solve() {
13     scanf("%s", s + 1);
14     int n = strlen(s + 1);
15     for (int i = 1; i <= n; i++) {
16         if (s[i] == '0') {
17             f[i][0] = f[i - 1][0];
18             f[i][1] = f[i - 1][1] + M;
19             f[i][2] = f[i - 1][2] + M + 1;
20         }
21         else {
22             f[i][0] = f[i - 1][0] + M + 1;
23             f[i][1] = f[i - 1][0];
24             f[i][2] = min(f[i - 1][1], f[i - 1][2]);
25         }
26     }
27     printf("%lld\n", min({f[n][0], f[n][1], f[n][2]}));
28 }
29 
30 int main() {
31     int t;
32     scanf("%d", &t);
33     while (t--) {
34         solve();
35     }
36     
37     return 0;
38 }

  还有一种贪心的做法。

  可以发现在由$0$和$1$构成的序列中,如果逆序对恰好为$1$,那么我们只需要执行一次交换操作就可以了。否则逆序对的数量超过$1$个,意味着至少存在一个数需要至少交换$2$次,因此直接删除这个数最优。这意味着我们最多只执行一次交换操作,其余的都执行删除操作。

  枚举分界点$i$将序列$s$分成前部分和后部分,要将$s$变成非递减序列,首先很容易想到把前部分$s[1 \sim i]$中所有的$1$删除,把后部分$s[i+1, n]$中所有的$0$删除。而只有当$s_i = 1, \ s_{i+1} = 0$时我们才交换$s_i$和$s_{i+1}$,此时代价就是$M$,而删除这两个元素的代价是$2 \cdot (M+1)$,明显交换会更优。而其他位置的交换呢?这时对于前部分在$j \in [1,i-1]$位置的元素,要将$j$位置上的元素交换到后部分的位置至少需要$2 \cdot M$,很明显不如直接删除来的更优。同理对于后部分在$j \in [i+2,n]$位置的元素,直接删除会更优。

  因此直接预处理$s$前缀中$1$的个数$f(i)$和后缀中$0$的个数$g(i)$,当枚举到分界点$i$,此时变成非递减序列的最小代价就是

$$\min \left\{ \left( f(i)+g(i) \right) \cdot (M+1), \ \left( f(i)+g(i)-2 \right) \cdot (M+1) + M \right\}$$

  注意只有$s_i = 1, \ s_{i+1} = 0$才能交换,即才有$\left( f(i)+g(i)-2 \right) \cdot (M+1) + M$。

  最后还要考虑前部分为$s[1 \sim n]$而后部分为空,和后部分为$s[1 \sim n]$而前部分为空的情况,这里的处理方法可以看代码实现。

  AC代码如下:

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 
 4 typedef long long LL;
 5 
 6 const int N = 3e5 + 10;
 7 const LL M = 1e12;
 8 
 9 char s[N];
10 int f[N], g[N];
11 
12 void solve() {
13     scanf("%s", s + 1);
14     int n = strlen(s + 1);
15     f[0] = g[n + 1] = 0;
16     for (int i = 1; i <= n; i++) {
17         f[i] = f[i - 1];
18         if (s[i] == '1') f[i]++;
19     }
20     for (int i = n; i; i--) {
21         g[i] = g[i + 1];
22         if (s[i] == '0') g[i]++;
23     }
24     LL ret = 1e18;
25     for (int i = 0; i <= n; i++) {
26         ret = min(ret, (f[i] + g[i + 1]) * (M + 1));
27         if (s[i] == '1' && s[i + 1] == '0') ret = min(ret, (f[i] + g[i + 1] - 2) * (M + 1) + M);
28     }
29     printf("%lld\n", ret);
30 }
31 
32 int main() {
33     int t;
34     scanf("%d", &t);
35     while (t--) {
36         solve();
37     }
38     
39     return 0;
40 }

 

参考资料

  Educational Codeforces Round 145 (Rated for Div. 2) A - D:https://zhuanlan.zhihu.com/p/616545904

  Educational Codeforces Round 145 Editorial:https://codeforces.com/blog/entry/114300

posted @ 2023-03-24 10:08  onlyblues  阅读(163)  评论(0编辑  收藏  举报
Web Analytics