[HEOI2016/TJOI2016] 求和

有关斯特林数的介绍和斯特林反演:斯特林数及斯特林反演

本来是想做容斥的,结果发现了一个多项式题目……

不过容斥和反演息息相关嘛。

这题做完之后感觉卷积也不是那么难,就把它理解成一个预处理一个复杂函数的方法就好了。这样复杂度可以从 \(O(n)\) 求和式变成 \(O(1)\) 取得函数值了。

这道题目是有关第二类斯特林数的,上文的博客中推导了第二类斯特林数的一个公式:

\(\begin{aligned} S(n, m) = \frac{1}{m!} \sum_{k = 0}^m (-1)^k C(m, k) (m - k)^n \\ = \sum_{k = 0}^m \frac{(-1)^k (m - k)^n}{k!(m - k)!} \end{aligned}\)

发现上式是个卷积形式,于是本题的柿子可以这样推导(后两步好神仙啊):

\(\begin{aligned} F(n) =\sum\limits_{i=0}^n \sum\limits_{j=0}^i S(i, j) * 2^j * j! \\ =\sum_{i=0}^n \sum_{j=0}^n S(i, j) * 2^j*j! \\ =\sum_{j=0}^n 2^j*j!\sum_{i=0}^n S(i, j) \\ =\sum_{j=0}^n 2^j*j!\sum_{i=0}^n \sum_{k=0}^j\frac{(-1)^k}{k!}\cdot\frac{(j-k)^i}{(j-k)!} \\ =\sum_{j=0}^n 2^j*j!\sum_{k=0}^j\frac{(-1)^k}{k!}\cdot\frac{ \sum_{i=0}^n (j-k)^i}{(j-k)!} \\ =\sum_{j=0}^n 2^j*j!\sum_{k=0}^j\frac{(-1)^k}{k!}\cdot \frac{(j-k)^{n+1}-1}{(j-k-1)(j-k)!} \\ \end{aligned}\)

这样,这道题目的柿子也化成了卷积形式:

\(\begin{aligned} f(x) = \frac{(-1)^x}{x!} \\ g(x) = \frac{x^{n+1}-1}{(x-1)*x!} \\ F(n) = \sum_{i = 0}^{n} 2^i * i! *(f×g)(i) \end{aligned}\)

可以用 \(NTT\) 求解 …… 抄抄抄

#include <cmath>
#include <queue>
#include <cstdio>
#include <cctype>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;

const int mod = 998244353;
const int maxn = 400000 + 10;
int n, len, rado, limit, r[maxn], fac[maxn], inv[maxn], f[maxn], g[maxn], pow_2[maxn], ans;

inline int Fast_pow(int x, int p) {
  register int res = 1;
  for( ; p; x = 1ll * x * x % mod, p = p >> 1) if( p & 1 ) res = 1ll * x * res % mod;
  return res;
}

inline void Fast_numbertheory_transform(int *a, int type) {
  for(int i = 0; i < limit; ++i) if( i < r[i] ) swap(a[i], a[r[i]]);
  for(int mid = 1; mid < limit; mid = mid << 1) {
    int Base_p = Fast_pow(3ll, (mod - 1) / (mid << 1));
    if( type == -1 ) Base_p = Fast_pow(Base_p, mod - 2);
    for(int l = 0, length = mid << 1; l < limit; l = l + length) {
      for(int k = 0, p = 1; k < mid; ++k, p = 1ll * p * Base_p % mod) {
        int x = a[l + k], y = 1ll * p * a[l + mid + k] % mod;
        a[l + k] = (x + y) % mod, a[l + mid + k] = (x - y + mod) % mod;
      }
    }
  }
  int inver = Fast_pow(limit, mod - 2);
  if( type == -1 ) for(int i = 0; i < limit; ++i) a[i] = 1ll * a[i] * inver % mod;
}


int main(int argc, char const *argv[])
{
  scanf("%d", &n), pow_2[0] = fac[0] = fac[1] = 1;
  for(int i = 1; i <= n; ++i) pow_2[i] = (pow_2[i - 1] << 1) % mod;
  for(int i = 2; i <= n; ++i) fac[i] = 1ll * i * fac[i - 1] % mod;
  inv[n] = Fast_pow(fac[n], mod - 2);
  for(int i = n; i >= 1; --i) inv[i - 1] = 1ll * inv[i] * i % mod;
  g[0] = 1, g[1] = n + 1;
  for(int i = 0; i <= n; ++i) f[i] = 1ll * ((i & 1) ? mod - 1 : 1) * inv[i] % mod;
  for(int i = 2; i <= n; ++i) g[i] = 1ll * (Fast_pow(i, n + 1) - 1 + mod) * Fast_pow(i - 1 + mod, mod - 2) % mod * inv[i] % mod;
  len = n << 1, rado = 0, limit = 1;
  while( limit < len ) limit = (limit << 1), ++rado;
  for(int i = 0; i < limit; ++i) r[i] = (r[i >> 1] >> 1) | ((i & 1) << (rado - 1));
  Fast_numbertheory_transform(f, 1);
  Fast_numbertheory_transform(g, 1);
  for(int i = 0; i < limit; ++i) f[i] = 1ll * f[i] * g[i] % mod;
  Fast_numbertheory_transform(f, -1);
  for(int i = 0; i <= n; ++i) ans = (ans + 1ll * fac[i] * pow_2[i] % mod * f[i] % mod) % mod;
  printf("%d\n", ans);

  return 0;
}
上一篇:科学思维与科学方法论--Matlab大作业 1.三次多项式逼近sinx问题;2、卫星运动轨迹;3、RLC振荡电路simulink


下一篇:canvas绘制波浪