[bzoj4555][Tjoi2016&Heoi2016]求和【stirling数】【FFT~NTT】

【题目链接】
  https://www.lydsy.com/JudgeOnline/problem.php?id=4555
【题解】
  考虑第二类斯特林数的公式:
   xn=xi=0(xi)i!Sn,i x n = ∑ i = 0 x ( i x ) i ! ∗ S n , i
  就是先枚举选了几个格子,再乘以顺序。
  对这个式子做二项式反演,得:
   Sn,x=1x!xi=0(1)xi(xi)in S n , x = 1 x ! ∑ i = 0 x ( − 1 ) x − i ( i x ) i n
  用此方法来化简问题中的式子:
   ni=0ij=0Si,j2jj! ∑ i = 0 n ∑ j = 0 i S i , j ∗ 2 j ∗ j !
   =ni=0ij=02jjk=0(1)jk(jk)ki = ∑ i = 0 n ∑ j = 0 i 2 j ∑ k = 0 j ( − 1 ) j − k ( k j ) k i
  由于当 i<j i < j Si,j=0 S i , j = 0 所以 j j 的枚举上界改为 n n
  同时展开组合数:
   =ni=0nj=02jj!jk=0(1)jk1k!(jk)!ki = ∑ i = 0 n ∑ j = 0 n 2 j ∗ j ! ∑ k = 0 j ( − 1 ) j − k 1 k ! ( j − k ) ! ∗ k i
  改变求和顺序:
   =nj=02jj!jk=0(1)jk(jk)!ni=0kik! = ∑ j = 0 n 2 j ∗ j ! ∑ k = 0 j ( − 1 ) j − k ( j − k ) ! ∑ i = 0 n k i k !
   =nj=02jj!k+t=j1tt!ni=0kik! = ∑ j = 0 n 2 j ∗ j ! ∑ k + t = j − 1 t t ! ∑ i = 0 n k i k !
  那么后面的和式就变成了卷积的形式,NTT即可。
  时间复杂度 O(NlogN) O ( N ∗ l o g N )
【代码】

/* - - - - - - - - - - - - - - -
    User :      VanishD
    problem :   [bzoj4555]
    Points :    stirling
- - - - - - - - - - - - - - - */
# include 
# define    ll      long long
# define    N       300010
using namespace std;
const int inf = 0x3f3f3f3f, INF = 0x7fffffff;
const ll  infll = 0x3f3f3f3f3f3f3f3fll, INFll = 0x7fffffffffffffffll;
int read(){
    int tmp = 0, fh = 1; char ch = getchar();
    while (ch < '0' || ch > '9'){ if (ch == '-') fh = -1; ch = getchar(); }
    while (ch >= '0' && ch <= '9'){ tmp = tmp * 10 + ch - '0'; ch = getchar(); }
    return tmp * fh;
}

const int P = 998244353, G = 3;
int mul[N], n, a[N], b[N], ans, l;
int power(int x, int y){
    int i = x; x = 1;
    while (y > 0){
        if (y % 2 == 1) x = 1ll * x * i % P;
        i = 1ll * i * i % P;
        y /= 2;
    }
    return x;
}
void NTT(int *a, int l, int tag){
    for (int i = 0, j = 0; i < l; i++){
        if (i < j) swap(a[i], a[j]);
        for (int k = (l >> 1); (j ^= k) < k; k >>= 1);
    }
    for (int i = 1; i < l; i *= 2){
        int wn = power(G, (P - 1) / (i * 2));
        if (tag == -1) wn = power(wn, P - 2);
        for (int j = 0; j < l; j += i * 2)
            for (int k = 0, w = 1; k < i; k++, w = 1ll * w * wn % P){
                int x = a[k + j], y = 1ll * w * a[k + i + j] % P;
                a[k + j] = (x + y) % P; a[k + i + j] = (x - y) % P;
            }
    }
    if (tag == -1){
        int inv = power(l, P - 2);
        for (int i = 0; i < l; i++) a[i] = 1ll * a[i] * inv % P;
    }
} 
int main(){
//  freopen(".in", "r", stdin);
//  freopen(".out", "w", stdout);
    n = read();
    mul[0] = 1;
    for (int i = 1; i <= n; i++) mul[i] = 1ll * mul[i - 1] * i % P; 
    for (int i = 0; i <= n; i++){
        a[i] = power(-1, i) * power(mul[i], P - 2);
        if (i == 1) b[i] = n + 1;
            else b[i] = 1ll * (power(i, n + 1) - 1) * power(i - 1, P - 2) % P * power(mul[i], P - 2) % P;
    }
    l = 1; while (l <= 2 * n) l <<= 1;
    NTT(a, l, 1); NTT(b, l, 1);
    for (int i = 0; i < l; i++) a[i] = 1ll * a[i] * b[i] % P;
    NTT(a, l, -1);
    for (int i = 0; i <= n; i++)
        ans = (ans + 1ll * power(2, i) *mul[i] % P * a[i]) % P;
    printf("%d\n", (ans + P) % P);
    return 0;
}

你可能感兴趣的:(【FFT~NTT】,【stirling数】)