[HEOI2016/TJOI2016] 求和

有關斯特林數的介紹和斯特林反演:斯特林數及斯特林反演html

原本是想作容斥的,結果發現了一個多項式題目……ios

不過容斥和反演息息相關嘛。函數

這題作完以後感受卷積也不是那麼難,就把它理解成一個預處理一個複雜函數的方法就行了。這樣複雜度能夠從 \(O(n)\) 求和式變成 \(O(1)\) 取得函數值了。spa

這道題目是有關第二類斯特林數的,上文的博客中推導了第二類斯特林數的一個公式:code

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

發現上式是個卷積形式,因而本題的柿子能夠這樣推導(後兩步好神仙啊):htm

\(\begin{aligned} \displaystyle F(n) =\sum_{i=0}^n \sum_{j=0}^i S(i, j) * 2^j * j! \\ \displaystyle =\sum_{i=0}^n \sum_{j=0}^n S(i, j) * 2^j*j! \\ \displaystyle =\sum_{j=0}^n 2^j*j!\sum_{i=0}^n S(i, j) \\ \displaystyle =\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)!} \\ \displaystyle =\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)!} \\ \displaystyle =\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}\)blog

這樣,這道題目的柿子也化成了卷積形式:get

\(\begin{aligned} \displaystyle f(x) = \frac{(-1)^x}{x!} \\ \displaystyle g(x) = \frac{x^{n+1}-1}{(x-1)*x!} \\ \displaystyle 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;
}
相關文章
相關標籤/搜索