UOJ269【清华集训2016】如何优雅地求和【数论,多项式】
题目描述:求
$$\sum_{k=0}^nf(k)\binom{n}{k}x^k(1-x)^{n-k}$$
输入$n$,$f(x)$的次数上界$m$,$x$,$f(0,1,\ldots,m)$,对$998244353$取模。
数据范围:$1\leq n\leq 10^9,1\leq m\leq 2*10^4,0\leq a_i,x<998244353$
现在来讲正解:首先我们把$f(x)$用下降幂来表示,(看到这里的快点关掉,然后自己推一推)
$$\begin{align*}Ans&=\sum_{k=0}^n\sum_{i=0}^ma_i*\frac{k!}{(k-i)!}\frac{n!}{k!(n-k)!}x^k(1-x)^{n-k} \\&=\sum_{k=0}^n\sum_{i=0}^ma_i*\frac{n!}{(k-i)!(n-k)!}x^k(1-x)^{n-k} \\&=\sum_{i=0}^ma_in!\sum_{k=i}^n\frac{x^k}{(k-i)!}*\frac{(1-x)^{n-k}}{(n-k)!} \\&=\sum_{i=0}^ma_ix^in!(e^x*e^{1-x}[x^{n-i}]) \\&=\sum_{i=0}^ma_ix^i\frac{n!}{(n-i)!}\end{align*}$$
然后就是考虑如何点值转下降幂了
$$\begin{align*}f(i)&=\sum_{j=0}^ma_j\frac{i!}{(i-j)!} \\\frac{f(i)}{i!}&=\sum_{j=0}^ma_j*\frac{1}{(i-j)!}\end{align*}$$
所以$f(i)$的EGF等于$a$的OGF乘上$e^x$,所以$a$的OGF等于$f(i)$的EGF乘上$e^{-x}$,使用NTT优化计算,时间复杂度$O(m\log m)$
1 #include<bits/stdc++.h> 2 #define Rint register int 3 using namespace std; 4 typedef long long LL; 5 const int N = 1 << 16, mod = 998244353, G = 3, Gi = 332748118; 6 int n, m, x, A[N], B[N], fac[N], invfac[N], ans; 7 inline int kasumi(int a, int b){ 8 int res = 1; 9 while(b){ 10 if(b & 1) res = (LL) res * a % mod; 11 a = (LL) a * a % mod; 12 b >>= 1; 13 } 14 return res; 15 } 16 inline void init(){ 17 fac[0] = 1; 18 for(Rint i = 1;i <= m;i ++) fac[i] = (LL) i * fac[i - 1] % mod; 19 invfac[m] = kasumi(fac[m], mod - 2); 20 for(Rint i = m;i;i --) invfac[i - 1] = (LL) i * invfac[i] % mod; 21 } 22 int rev[N]; 23 inline int calrev(int len){ 24 int limit = 1, L = -1; 25 while(limit <= len){limit <<= 1; L ++;} 26 for(Rint i = 0;i < limit;i ++) 27 rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << L); 28 return limit; 29 } 30 inline void NTT(int *A, int limit, int type){ 31 for(Rint i = 0;i < limit;i ++) 32 if(i < rev[i]) swap(A[i], A[rev[i]]); 33 for(Rint mid = 1;mid < limit;mid <<= 1){ 34 int Wn = kasumi(type == 1 ? G : Gi, (mod - 1) / (mid << 1)); 35 for(Rint j = 0;j < limit;j += (mid << 1)){ 36 int w = 1; 37 for(Rint k = 0;k < mid;k ++, w = (LL) w * Wn % mod){ 38 int x = A[j + k], y = (LL) w * A[j + k + mid] % mod; 39 A[j + k] = (x + y) % mod; 40 A[j + k + mid] = (x - y + mod) % mod; 41 } 42 } 43 } 44 if(type == -1){ 45 int inv = kasumi(limit, mod - 2); 46 for(Rint i = 0;i < limit;i ++) 47 A[i] = (LL) A[i] * inv % mod; 48 } 49 } 50 int main(){ 51 scanf("%d%d%d", &n, &m, &x); 52 init(); 53 for(Rint i = 0;i <= m;i ++){ 54 scanf("%d", A + i); 55 A[i] = (LL) A[i] * invfac[i] % mod; 56 B[i] = invfac[i]; 57 if(i & 1) B[i] = mod - B[i]; 58 } 59 int limit = calrev(m << 1); 60 NTT(A, limit, 1); NTT(B, limit, 1); 61 for(Rint i = 0;i < limit;i ++) A[i] = (LL) A[i] * B[i] % mod; 62 NTT(A, limit, -1); 63 int w = 1; 64 for(Rint i = 0;i <= m;i ++){ 65 ans = (ans + (LL) w * A[i] % mod) % mod; 66 w = (LL) w * (mod + n - i) % mod * x % mod; 67 } 68 printf("%d", ans); 69 }
启发:推柿子遇到多项式时应考虑多项式的各种形式,如点值,普通/上升/下降幂等。