Codeforces Round #692 Div1 B. Grime Zoo(贪心)

B. Grime Zoo

题意

给一个由01? 组成的字符串,子序列01会产生 x x x 的贡献,子序列10 会产生 y y y 的贡献,要求把所有的 ? 变成 01,且总贡献最小,求最小贡献。

解法

假设有 c 0 c_0 c0? 变成 0 ,有 c 1 c_1 c1? 变成 1 。即每个数字的数量确定了,那么可以发现,子序列00的个数是确定的,子序列11 的个数是确定的,那么 0110 的个数之和是定值,如果 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] ++++
**/
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值