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 }
UOJ269

 启发:推柿子遇到多项式时应考虑多项式的各种形式,如点值,普通/上升/下降幂等。

posted @ 2019-07-10 22:10  mizu164  阅读(491)  评论(0编辑  收藏  举报