CF2048H Kevin and Strange Operation

操作的自由度很大,打表可以发现限制操作的位置只增不减也是对的。

考虑怎么判断一个串 \(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;
}
posted @ 2025-09-18 22:10  CJzdc  阅读(20)  评论(0)    收藏  举报