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 1012 coins;
  • choose any element from the string and remove it. In order to perform this operation, you pay 1012+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 s1s2sm, 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 (1t104) — the number of test cases.

The only line of each test case contains the string s (1|s|3105), consisting of only characters 0 and/or 1.

The sum of lengths of all given strings doesn't exceed 3105.

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{0,1,2}根据前一次操作后的状态f(i1,k), k{0,1,2}来转移得到。定义M=1012

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

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

si=0:{f(i,0)=f(i1,0)f(i,1)=f(i1,1)+Mf(i,2)=f(i1,2)+M+1si=1:{f(i,0)=f(i1,0)+M+1f(i,1)=f(i1,0)f(i,2)=min{f(i1,1), f(i1,2)}

  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 }
复制代码

  还有一种贪心的做法。

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

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

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

min{(f(i)+g(i))(M+1), (f(i)+g(i)2)(M+1)+M}

  注意只有si=1, si+1=0才能交换,即才有(f(i)+g(i)2)(M+1)+M

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

  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 @   onlyblues  阅读(175)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效
历史上的今天:
2022-03-24 砝码称重
2022-03-24 社交距离 I
Web Analytics
点击右上角即可分享
微信分享提示