CF1450H2 Multithreading (Hard Version)

先考虑怎么刻画序列的 \(f\)

考虑把 w 作为 \(0\),b 作为 \(1\)

每次我们可以删 00 或 11,最后剩下的 0101... 状物的长度除以 \(4\) 就是 \(f\) 的值。

每次删长度 \(2\) 的串,这对每个位置的奇偶性是不影响的。

考虑把奇数位取反,那么每次可以删 01 或 10,最后剩同色段就是答案。

显然这个同色段长度就是奇数位取反后 0 个数与 1 个数之差。

\(u_{0/1}\) 为未知的奇/偶位数量,\(v\) 为已知位权值和。

一个想法是,考虑枚举 \(i\)\(j\) 为 0 在奇偶各选多少个,然后就是一个卷积的形式,可以做到 \(O(n\log n)\)

另一个想法是,直接枚举剩余位带权和 \(d\),然后设为正的有 \(i\) 个,负的有 \(j\) 个,那么方案数就是 \(\sum\limits_{i-j=d}\dbinom{u_0}{i}\dbinom{u_1}{j}\),范德蒙德卷积做到线性。

H1
#include <bits/stdc++.h>
using namespace std;
using ll = long long;

const int mod = 998244353;
const int inv2 = (mod + 1) / 2;
void Add(int &x, ll y) { x = (x + y) % mod; }
int Pow(int x, int y) {
  int b = x, r = 1;
  for(; y; b = (ll)b * b % mod, y /= 2) {
    if(y & 1) r = (ll)r * b % mod;
  }
  return r;
}
const int kN = 2e5 + 5;
int n, q;
string s;
int mul[kN], imul[kN];

void Init(int N = kN - 1) {
  mul[0] = 1;
  for(int i = 1; i <= N; i++) mul[i] = (ll)mul[i - 1] * i % mod;
  imul[N] = Pow(mul[N], mod - 2);
  for(int i = N - 1; ~i; i--) imul[i] = (ll)imul[i + 1] * (i + 1) % mod;
}
int C(int n, int m) {
  if((n < m) || (m < 0)) return 0;
  return (ll)mul[n] * imul[m] % mod * imul[n - m] % mod;
}

int main() {
//  freopen("1.in", "r", stdin);
//  freopen("1.out", "w", stdout);
  ios::sync_with_stdio(0), cin.tie(0);
  Init();
  cin >> n >> q >> s;
  s = " " + s;
  int u[2] = {0, 0};
  int v = 0;
  for(int i = 1; i <= n; i++) {
    if(s[i] == '?') u[i & 1]++;
    else ((s[i] == 'b') ^ (i & 1)) ? v++ : v--;
  }
  v = (v + u[0] - u[1]) / 2;
  int ans = 0;
  for(int d = -n; d <= n; d++) {
    if((d & 1) != (v & 1)) continue;
    Add(ans, (ll)abs(d - v) * C(u[0] + u[1], u[1] + d));
  }
  cout << (ll)ans * Pow(inv2, u[0] + u[1]) % mod << "\n";
  return 0;
}

我们重新整理一下式子:给定 \(A,B,V\),求 \(\sum\limits_{d\equiv V\pmod 2}|d-V|\dbinom{A+B}{B+d}\)

考虑枚举 \(k=|d-V|\),则需求 \(\sum\limits_{k\ge 1}2k\left(\dbinom{A+B}{B+V+2k}+\dbinom{A+B}{B+V-2k}\right)\)

不妨考虑 \(\dbinom{A+B}{B+V-2k}\),前面的同理。

\( \begin{aligned} \sum\limits_{k\ge 1}2k\dbinom{A+B}{B+V-2k} &= (B+V)\sum\limits_{k\ge 1}\dbinom{A+B}{B+V-2k}-\sum\limits_{k\ge 1}(B+V-2k)\dbinom{A+B}{B+V-2k}\\ &= (B+V)\sum\limits_{k\ge 1}\dbinom{A+B}{B+V-2k}-(A+B)\sum\limits_{k\ge 1}\dbinom{A+B-1}{B+V-2k-1} \end{aligned} \)

考虑定义 \(F(A,B,V)=\sum\limits_{k\ge 0}\dbinom{A+B}{B+V-2k}\)

上式为 \((B+V)F(A,B,V-2)-(A+B)F(A,B-1,V-2)\)

考虑 \(F\) 怎么求。

\(\sum\limits_{k\ge 0}\left(\dbinom{A+B}{B+V-2k}+\dbinom{A+B}{B+V-2k-1}\right)=\sum\limits_{k\ge 0}\dbinom{A+B}{B+V-k}\) 是可以递推的。

\(\sum\limits_{k\ge 0}\left(\dbinom{A+B}{B+V-2k}-\dbinom{A+B}{B+V-2k-1}\right)=\dbinom{A+B-1}{B+V}\)

注意到单点改对于 \(A,B,V\) 的修改都是 \(O(1)\) 的,所以直接转移即可。复杂度线性。

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

const int mod = 998244353;
const int inv2 = (mod + 1) / 2;
void Add(int &x, ll y) { x = (x + y) % mod; }
int Pow(int x, int y) {
  int b = x, r = 1;
  for(; y; b = (ll)b * b % mod, y /= 2) {
    if(y & 1) r = (ll)r * b % mod;
  }
  return r;
}
const int kN = 2e5 + 5;
int n, q;
string s;
int pw2[kN], ipw2[kN], mul[kN], imul[kN];

struct Node {
  int A, B, V;
  Node() { }
  Node(int _A, int _B, int _V) {
    A = _A;
    B = _B;
    V = _V;
  }
};
Node qry[kN];

void Init(int N = kN - 1) {
  mul[0] = 1;
  for(int i = 1; i <= N; i++) mul[i] = (ll)mul[i - 1] * i % mod;
  imul[N] = Pow(mul[N], mod - 2);
  for(int i = N - 1; ~i; i--) imul[i] = (ll)imul[i + 1] * (i + 1) % mod;
  pw2[0] = ipw2[0] = 1;
  for(int i = 1; i <= N; i++) {
    pw2[i] = 2ll * pw2[i - 1] % mod;
    ipw2[i] = (ll)ipw2[i - 1] * inv2 % mod;
  }
}
int C(int n, int m) {
  if((n < m) || (m < 0)) return 0;
  return (ll)mul[n] * imul[m] % mod * imul[n - m] % mod;
}

int cx, cy, cs = 1;
void AddX() { cs = (2ll * cs + mod - C(cx++, cy)) % mod; }
void DelX() { cs = (ll)inv2 * (cs + C(--cx, cy)) % mod; }
void AddY() { Add(cs, C(cx, ++cy)); }
void DelY() { Add(cs, mod - C(cx, cy--)); }
int F(int A, int B, int V) {
  int sum = 0;
  int x = A + B;
  int y = B + V;
  while(cx < x) AddX();
  while(cx > x) DelX();
  while(cy < y) AddY();
  while(cy > y) DelY();
  return (ll)inv2 * (cs + C(A + B - 1, B + V)) % mod;
}

int Calc(int A, int B, int V) {
  int ans = 0;
  if(A + B == 1) {
    if(((B + V) & 1) && (B + V < 0)) ans++;
    if(((B + V) & 1) && (B + V >= 3)) ans--;
  }else {
    Add(ans, (ll)(A + B) * pw2[A + B - 2]);
    Add(ans, (ll)(mod - A - B) * F(A, B - 1, V));
    Add(ans, (ll)(mod - A - B) * F(A, B - 1, V - 2));
  }
  Add(ans, (ll)(mod - B - V) * pw2[A + B - 1]);
  Add(ans, (ll)(B + V) * F(A, B, V));
  Add(ans, (ll)(B + V) * F(A, B, V - 2));
  return ans;
}

int main() {
//  freopen("1.in", "r", stdin);
//  freopen("1.out", "w", stdout);
  ios::sync_with_stdio(0), cin.tie(0);
  Init();
  cin >> n >> q >> s;
  s = " " + s;
  int u[2] = {0, 0};
  int v = 0;
  auto Upd = [&](int x, int c) -> void {
    if(s[x] == '?') u[x & 1] += c;
    else ((s[x] == 'b') ^ (x & 1)) ? (v += c) : (v -= c);
  };
  for(int i = 1; i <= n; i++) Upd(i, 1);
  qry[0] = Node(u[0], u[1], (v + u[0] - u[1]) / 2);
  for(int i = 1; i <= q; i++) {
    int x;
    char c;
    cin >> x >> c;
    Upd(x, -1), s[x] = c, Upd(x, 1);
    qry[i] = Node(u[0], u[1], (v + u[0] - u[1]) / 2);
  }
  for(int i = 0; i <= q; i++) {
    int A = qry[i].A;
    int B = qry[i].B;
    int V = qry[i].V;
    cout << (ll)Calc(A, B, V) * ipw2[A + B] % mod << "\n";
  }
  return 0;
}
posted @ 2025-09-06 15:37  CJzdc  阅读(16)  评论(0)    收藏  举报