Solution -「ABC 246H」01? Queries

cirnovsky /

link。

平时基本打不到 ex,这个 ex 还是比较 ez 的,但也有些需要注意的地方。

考虑 dp 规划前缀,设 f[i][0/1]f[i][0/1] 表示前缀 [1,i][1, i] 否是选 ii 的方案数,这个明显会算重,注意到前缀 [1,i)[1, i) 凑不出来的字符串可以通过前缀 [1,i)[1, i) 再 append 一个 0/10/1 凑出来,所以我们改描述 f[i][0/1]f[i][0/1] 为前缀 [1,i][1, i] 凑出结尾为 0/1\texttt0/\texttt1 的方案数。

转移分 sis_i0/1/?\texttt{0}/\texttt{1}/\texttt{?} 讨论,放到矩阵上做 ddp 即可,如果令 f[0][0]=f[0][1]:=1f[0][0] = f[0][1] := 1 可以缩减矩阵的规模至 22,但是这样答案就要减 22

这类 dp 通常对当前元素的直接规划会算重,可以将目光放到已经构造出的结果上,再结合结论(其实比较 ad hoc 了)避免算重。

// s[i]=0: dp[i][0] = dp[i-1][0]+dp[i-1][1], dp[i][1] = dp[i-1][1]
// s[i]=1: dp[i][0] = dp[i-1][0], dp[i][1] = dp[i-1][0]+dp[i-1][1]
// s[i]=?: dp[i][0/1] = dp[i-1][0]+dp[i-1][1]
using modint = modint998244353;
using mt = static_matrix<modint, 2>;
int n, q, ms, mh;
char s[100100];
vi<mt> md;
mt get(int i) {
    return mt({{1, s[i-1] == '1' || s[i-1] == '?'}, {s[i-1] == '0' || s[i-1] == '?', 1}});
}
void upd(int now) {
    md[now] = md[now*2]*md[now*2+1];
}
void mdf(int now, const mt& w) {
    md[now += ms-1] = w;
    for (int i=1; i<=mh; ++i) {
        upd(now>>i);
    }
}
signed main() {
    ios::sync_with_stdio(0);
    cin.tie(0);
    cin >> n >> q >> s;
    mh = ceil(log2(n)), ms = 1<<mh;
    md = vi<mt>(ms*2, mt::id());
    for (int i=1; i<=n; ++i) {
        md[i+ms-1] = get(i);
    }
    for (int i=ms-1; i>=1; --i) {
        upd(i);
    }
    char c;
    for (int x; q--;) {
        cin >> x >> c;
        s[x-1] = c;
        mdf(x, get(x));
        mt ret = mt({{1, 1}, {0, 0}})*md[1];
        cout << (ret[0][0]+ret[0][1]-2).val() << "\n";
    }
}