CF1609 E. William The Oblivious

CF1609 E. William The Oblivious

题意

给定一个长度为 n n n的字符串 s s s,字符串内只含有 a , b , c a,b,c a,b,c三种字符,即 ∀ i , s [ i ] \forall i,s[i] i,s[i] a , b , c a,b,c a,b,c中一个。
q q q次询问,每次询问会将 s [ p o s ] s[pos] s[pos]修改成 v v v,即 s [ p o s ] = v s[pos]=v s[pos]=v

你可以进行一种操作,使得任意一个位置的 s [ i ] s[i] s[i]转变成 a , b , c a,b,c a,b,c中的一个字符。

对于每次询问,需要回答:将当前字符串修改成子序列中不含abc的最小操作次数。

思路

线段树维护每一个连续子区间的信息,定义状态为:

  • d p [ r o o t ] [ 0 ] dp[root][0] dp[root][0]:将当前子串修改成子序列中不含a的最小操作次数。
  • d p [ r o o t ] [ 1 ] dp[root][1] dp[root][1]:将当前子串修改成子序列中不含b的最小操作次数。
  • d p [ r o o t ] [ 2 ] dp[root][2] dp[root][2]:将当前子串修改成子序列中不含c的最小操作次数。
  • d p [ r o o t ] [ 3 ] dp[root][3] dp[root][3]:将当前子串修改成子序列中不含ab的最小操作次数。
  • d p [ r o o t ] [ 4 ] dp[root][4] dp[root][4]:将当前子串修改成子序列中不含bc的最小操作次数。
  • d p [ r o o t ] [ 5 ] dp[root][5] dp[root][5]:将当前子串修改成子序列中不含abc的最小操作次数。

则状态转移为:

root.dp[0] = left.dp[0] + right.dp[0];
root.dp[1] = left.dp[1] + right.dp[1];
root.dp[2] = left.dp[2] + right.dp[2];
root.dp[3] = min(left.dp[3] + right.dp[1], left.dp[0] + right.dp[3]);
root.dp[4] = min(left.dp[1] + right.dp[4], left.dp[4] + right.dp[2]);
root.dp[5] = min(left.dp[0] + right.dp[5],
                 min(left.dp[5] + right.dp[2], left.dp[3] + right.dp[4]));

代码

#include <bits/stdc++.h>
using namespace std;

const int N = 1e5 + 10;
const int inf = 0x3f3f3f3f;

int n, q;
char s[N];

struct Tree {
    int l, r;
    int dp[6];
} tr[N * 4];

void pushup(int p) {
    auto &root = tr[p], &left = tr[p << 1], &right = tr[p << 1 | 1];
    root.dp[0] = left.dp[0] + right.dp[0];
    root.dp[1] = left.dp[1] + right.dp[1];
    root.dp[2] = left.dp[2] + right.dp[2];
    root.dp[3] = min(left.dp[3] + right.dp[1], left.dp[0] + right.dp[3]);
    root.dp[4] = min(left.dp[1] + right.dp[4], left.dp[4] + right.dp[2]);
    root.dp[5] = min(left.dp[0] + right.dp[5],
                     min(left.dp[5] + right.dp[2], left.dp[3] + right.dp[4]));
}

void build(int p, int l, int r) {
    tr[p] = {l, r};
    if (l == r) {
        memset(tr[p].dp, 0, sizeof tr[p].dp);
        tr[p].dp[s[l] - 'a'] = 1;
        return;
    }
    int mid = (tr[p].l + tr[p].r) >> 1;
    build(p << 1, l, mid);
    build(p << 1 | 1, mid + 1, r);
    pushup(p);
}

void modify(int p, int x, char v) {
    if (tr[p].l == tr[p].r && tr[p].l == x) {
        s[x] = v;
        memset(tr[p].dp, 0, sizeof tr[p].dp);
        tr[p].dp[s[x] - 'a'] = 1;
        return;
    }
    int mid = (tr[p].l + tr[p].r) >> 1;
    if (x <= mid)
        modify(p << 1, x, v);
    else
        modify(p << 1 | 1, x, v);
    pushup(p);
}

int main() {
    cin >> n >> q;
    cin >> s + 1;
    build(1, 1, n);

    while (q--) {
        int x, res = inf;
        char val;
        cin >> x >> val;
        modify(1, x, val);

        cout << tr[1].dp[5] << "\n";
    }

    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值