ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

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

2020-11-21 17:02:59  阅读:200  来源: 互联网

标签:std NOI LibreOJ coef int len times LOJ575 inline


题意:给定字符串 \(\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;
}

标签:std,NOI,LibreOJ,coef,int,len,times,LOJ575,inline
来源: https://www.cnblogs.com/HolyK/p/14016291.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有