[LOJ575]「LibreOJ NOI Round #2」不等关系

题意:给定字符串 \(\overline{s_1s_2\dots s_n}\),仅包含 <> 两种字符,计算 使得 \(p_i < p_{i + 1}\) 当且仅当 \(s_i\) 为 \(<\) 的排列 \(p_1, p_2, \dots, p_{n + 1}\) 的数量对 \(998244353\) 取模的结果。

我魔改了 [HEOI2013]SAO 的dp计数题居然直接撞原题,且原题还加强了数据范围,变成多项式题了。

把字符串看成被 > 分割成的一段段连续的 <

\[\overline{<<<<}>\overline{<<}>\overline{<<<<<<}\cdots \]

设第 \(i\) 段长度为 \(len_i\),现在考虑仅满足 < 的方案数(即 > 的左边未必大于右边),容易发现这是一个多项式系数 \(\dbinom{n + 1}{len_1, len_2, len3, \dots} = \dfrac{(n + 1)!}{\prod len_i!}\)。

这样可以容斥了:枚举一些 > 强制转成 < 计算方案数,然后减掉(或加上),可以得到一个 \(O(2^k\times n)\) 的优秀算法(k是 > 的个数)。

把 \((n + 1)!\) 提出来,当 \(s_i = '>'\) 时,设

\[f[i] = \sum coef\times\frac{1}{\prod len_i} \]

可以得到转移,其中 \(cnt[i]\) 表示前 \(i\) 个中 > 的个数

\[f[i] = \sum_{s_j = '>'}(-1)^{cnt[i - 1] - cnt[j]}\times f[j]\times \frac1{(i - j)!} \]

答案是 \(f[n + 1] \times (n + 1)!\) 。

设 \(A_i = f[i] \times (-1)^{cnt[i]}\),上式可以化成一个卷积的形式,用分治fft优化。

#include <bits/stdc++.h>
template <class T>
inline void readInt(T &w) {
  char c, p = 0;
  while (!isdigit(c = getchar())) p = c == '-';
  for (w = c & 15; isdigit(c = getchar());) w = w * 10 + (c & 15);
  if (p) w = -w;
}
template <class T, class... U>
inline void readInt(T &w, U &... a) { readInt(w), readInt(a...); }

constexpr int P(998244353), G(3);
inline void inc(int &x, int y) { (x += y) >= P ? x -= P : 0; }
inline int sum(int x, int y) { return x + y >= P ? x + y - P : x + y; }
inline int sub(int x, int y) { return x - y < 0 ? x - y + P : x - y; }
inline int fpow(int x, int k = P - 2) {
  int r = 1;
  for (; k; k >>= 1, x = 1LL * x * x % P)
    if (k & 1) r = 1LL * r * x % P;
  return r;
}

namespace Polynomial {
using Polynom = std::vector<int>;
std::vector<int> w, inv = {1, 1};
void updateInv(int n) {
  if ((int)inv.size() <= n) {
    int p = inv.size();
    inv.resize(n + 1);
    for (int i = p; i <= n; i++) inv[i] = 1LL * (P - P / i) * inv[P % i] % P;
  }
}
void getOmega(int k) {
  w.resize(k);
  w[0] = 1;
  int base = fpow(G, (P - 1) / (k << 1));
  for (int i = 1; i < k; i++) w[i] = 1LL * w[i - 1] * base % P;
}
void dft(Polynom &a) {
  for (int n = a.size(), k = n >> 1; k; k >>= 1) {
    getOmega(k);
    for (int i = 0; i < n; i += k << 1) {
      for (int j = 0; j < k; j++) {
        int y = a[i + j + k];
        a[i + j + k] = (1LL * a[i + j] - y + P) * w[j] % P;
        inc(a[i + j], y);
      }
    }
  }
}
void idft(Polynom &a) {
  int n = a.size(), inv = P - (P - 1) / n;
  for (int k = 1; k < n; k <<= 1) {
    getOmega(k);
    for (int i = 0; i < n; i += k << 1) {
      for (int j = 0; j < k; j++) {
        int x = a[i + j], y = 1LL * a[i + j + k] * w[j] % P;
        a[i + j] = sum(x, y), a[i + j + k] = sub(x, y);
      }
    }
  }
  for (int i = 0; i < n; i++) a[i] = 1LL * a[i] * inv % P;
  std::reverse(a.begin() + 1, a.end());
}

Polynom operator*(Polynom a, Polynom b) {
  int len = a.size() + b.size() - 1;
  if (a.size() < 5 || b.size() < 5) {
    Polynom c(len);
    for (unsigned i = 0; i < a.size(); i++)
      for (unsigned j = 0; j < b.size(); j++)
        c[i + j] = 1LL * (c[i + j] + 1LL * a[i] * b[j]) % P;
    return c;
  }
  int n = 1 << std::__lg(len - 1) + 1;
  a.resize(n), b.resize(n);
  dft(a), dft(b);
  for (int i = 0; i < n; i++) a[i] = 1LL * a[i] * b[i] % P;
  idft(a);
  a.resize(len);
  return a;
}
} // namespace Polynomial
using Polynomial::Polynom;
using Polynomial::operator*;

constexpr int N(1e5 + 5);
int n, f[N], fac[N], ifac[N], coef[N];
char s[N];

void solve(int l, int r) {
  if (l == r) return;
  int mid = l + r >> 1;
  solve(l, mid);
  Polynom a(f + l, f + mid + 1), b(ifac, ifac + r - l + 1);
  a = a * b;
  for (int i = mid + 1; i <= r; i++)
    f[i] = s[i] == '>' ? sub(f[i], a[i - l]) : 0;
  solve(mid + 1, r);
}
int main() {
  std::ios::sync_with_stdio(false);
  std::cin.tie(nullptr);
  std::cin >> s + 1;
  n = strlen(s + 1) + 1;
  fac[0] = 1;
  for (int i = 1; i <= n; i++) fac[i] = 1LL * fac[i - 1] * i % P;
  ifac[n] = fpow(fac[n]);
  for (int i = n; i; i--) ifac[i - 1] = 1LL * ifac[i] * i % P;
  s[0] = s[n] = '>';
  coef[0] = 1;
  for (int i = 1; i <= n; i++) {
    coef[i] = s[i] == '>' ? -coef[i - 1] : coef[i - 1];
  }
  f[0] = 1;
  solve(0, n);
  int ans = 1LL * coef[n] * f[n] * fac[n] % P;
  if (ans < 0) ans += P;
  std::cout << ans;
  return 0;
}
上一篇:写一个简单的插入排序


下一篇:手机小游戏辅助程序的实现