操作的自由度很大,打表可以发现限制操作的位置只增不减也是对的。
考虑怎么判断一个串 \(t\) 是否合法。
观察到对于一个位置 \(i\) 满足 \(s_i=0\),想要通过操作使 \(s_i\) 变为 \(1\),只需要 \(>i\) 的位置删掉了 \(\ge c_i\) 个数。其中 \(c_i\) 为 \(>i\) 的第一个 \(s=1\) 位置与 \(i\) 的距离。
那么可以去倒着对 \(t\) 做匹配,每次如果当前的 \(s_i\) 和 \(t\) 的最后一位不同,则需要删除 \(i\)。
考虑对匹配的过程 dp。\(f_{i,j}\) 表示删除了 \(i\),且 \(i\sim n\) 有 \(j\) 个位置被删除。
注意到如果 \(s_i=1\),那么我们需要向前找到第一个 \(s_p=0\) 的位置 \(p\)。而此时 \(p+1\sim i\) 都会被删除,于是需要 \(c_p\ge j+i-p\),这和 \(s_i=1\) 是矛盾的。于是有 \(s_i=0\)。
考虑 dp 怎么转移。我们已知了 \(s_i=0\),那么当前 \(t\) 的结尾一定是 \(1\),而 \(j<c_i\),所以 \(t\) 一定会匹配到 \(i\) 之前的第一个 \(1\) 位置 \(p\)。\(p+1\sim i\) 一定都会被删除。枚举下一个被删的位 \(q\),需要满足 \(q<p\),且 \(c_q\ge j+i-p\)。然后 \(f_{i,j}\to f_{q,j+i-p}\)。
这个的状态数已经 \(O(n^2)\) 了,考虑优化。
注意到 \(c_i\) 是和后面第一个 \(1\) 有关的,考虑把 \(0\) 缩连续段。一个连续段内不会有转移,并且此时状态数就是 \(O(n)\) 的了。
这时上面的转移就是若干次区间加,差分即可。复杂度 \(O(n)\)。
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
using pii = pair<int, int> ;const int mod = 998244353;
void Add(int &x, ll y) { x = (x + y) % mod; }
const int kN = 1e6 + 5;
int n;
string s;
int d[kN];
bool a[kN];
vector<int> f[kN];struct Node {int l, r, v;Node() { }Node(int _l, int _r, int _v) {l = _l;r = _r;v = _v;}
}seg[kN];void Solve() {cin >> s;n = s.size(), s = " " + s;fill(d + 1, d + n + 1, 0);fill(f + 1, f + n + 1, vector<int> ());for(int i = 1; i <= n; i++) a[i] = (s[i] == '1');int m = 0;for(int l = 1, r; l <= n; l = r + 1) {for(r = l; (r < n) && (a[r + 1] == a[r]); r++) ;seg[++m] = Node(l, r, a[l]);}int ans = n;for(int i = m; i; i--) {if(seg[i].v) continue;int l = seg[i].l, r = seg[i].r;int len = r - l + 1;f[i].resize(len + 1, 0);for(int j = 1, s = 0; j <= len; j++) {Add(s, d[j]);f[i][j] = s + (j == 1);Add(ans, (ll)f[i][j] * (l - 1) % mod * (len - j + 1));}for(int j = 1; j <= len; j++) {Add(d[j + 1], f[i][j]);Add(d[len + 2], mod - f[i][j]);}}cout << ans << "\n";
}int main() {
// freopen("1.in", "r", stdin);
// freopen("1.out", "w", stdout);ios::sync_with_stdio(0), cin.tie(0);int t;cin >> t;while(t--) Solve(); return 0;
}