B. Grime Zoo
题意
给一个由0
、1
、?
组成的字符串,子序列01
会产生
x
x
x 的贡献,子序列10
会产生
y
y
y 的贡献,要求把所有的 ?
变成 0
或 1
,且总贡献最小,求最小贡献。
解法
假设有
c
0
c_0
c0 个 ?
变成 0
,有
c
1
c_1
c1 个 ?
变成 1
。即每个数字的数量确定了,那么可以发现,子序列00
的个数是确定的,子序列11
的个数是确定的,那么 01
和 10
的个数之和是定值,如果
x
>
y
x>y
x>y ,那么把
1
1
1 尽可能的放在前面,把
0
0
0 尽可能地放在后面,反之把
0
0
0 尽可能的放在前面,把
1
1
1 尽可能地放在后面。可以先假设所有的 ?
都是 1
,如果
x
>
y
x>y
x>y ,就把由 ?
变成的
1
1
1 从后往前的变成
0
0
0 ,如果
x
<
y
x<y
x<y ,就把由 ?
变成的
1
1
1 从前往后的变成
0
0
0 。维护最小值即可。
代码
#pragma region
//#pragma optimize("Ofast")
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <map>
#include <queue>
#include <set>
#include <vector>
using namespace std;
typedef long long ll;
#define rep(i, a, n) for (int i = a; i <= n; ++i)
#define per(i, a, n) for (int i = n; i >= a; --i)
#pragma endregion
const int maxn = 1e5 + 5;
char s[maxn];
int pre[2][maxn], suf[2][maxn];
int main() {
scanf("%s", s + 1);
ll x, y;
scanf("%lld%lld", &x, &y);
int n = strlen(s + 1);
vector<int> pos;
rep(i, 1, n) {
rep(j, 0, 1) pre[j][i] = pre[j][i - 1];
s[i] == '0' ? pre[0][i]++ : pre[1][i]++;
if (s[i] == '?') pos.push_back(i);
}
per(i, 1, n) {
rep(j, 0, 1) suf[j][i] = suf[j][i + 1];
s[i] == '0' ? suf[0][i]++ : suf[1][i]++;
}
ll tmp = 0;
rep(i, 1, n) {
if (s[i] == '0')
tmp += x * suf[1][i + 1]; //01
else
tmp += y * suf[0][i + 1]; //10
}
ll ans = tmp;
if (x > y) {
int deltsuf[2] = {0};
per(i, 1, n) {
// 1 -> 0
if (s[i] != '?') continue;
rep(j, 0, 1) suf[j][i + 1] += deltsuf[j];
tmp += x * (suf[1][i + 1] - pre[0][i - 1]); //01
tmp += y * (pre[1][i - 1] - suf[0][i + 1]); //10
deltsuf[1]--, deltsuf[0]++;
ans = min(ans, tmp);
}
} else {
int deltpre[2] = {0};
rep(i, 1, n) {
if (s[i] != '?') continue;
rep(j, 0, 1) pre[j][i - 1] += deltpre[j];
tmp += x * (suf[1][i + 1] - pre[0][i - 1]); //01
tmp += y * (pre[1][i - 1] - suf[0][i + 1]); //10
deltpre[1]--, deltpre[0]++;
ans = min(ans, tmp);
}
}
printf("%lld\n", ans);
}
/**
* 一开始全是1
xxxxxx000111xxxx
1->0
01 += suf1[i] - pre0[i] ----
10 += pre1[i] - suf0[i] ++++
**/