題解 P6667/BZOJ4734 【[清華集訓2016] 如何優雅地求和】
阿新 • • 發佈:2021-01-19
簡化題面
已知 \(m\) 次多項式函式 \(f\) 在 \(0,1\dots,m\) 處的取值\(f(0),f(1),\dots,f(m)\),給定\(n,x\),求
\[\sum\limits_{k=0}^{n}f(k)\binom{n}{k}x^{k}(1-x)^{n-k} \]答案對\(998244353\)取模
解題思路
看起來很不好求,我們可以把\(f\)轉成下降冪\(f(x)=\sum_{i=0}^{m}f_i\times x(i)\)
其中我們有\(x(i)=\prod_{k=0}^{i-1}(x-k)=\frac{x!}{(x-k)!}\)
那麼原柿子可以寫成
\[\sum\limits_{k=0}^{n}f(k)\binom{n}{k}x^{k}(1-x)^{n-k} \]然後你看到後面那一坨\(k\)是從\(i\)開始的,轉變列舉範圍後面就變成了
\[\sum_{k=0}^{n-i}\binom{n-i}{k}x^k(1-k)^{n-k-i} \]這不是二項式定理嗎= =,然後可以求得後面那一堆柿子恆等於\((1-k+k)^{n-i}=1\)
那麼我們的柿子就變成了
\[\sum_{i=0}^{m}f_in(i)x^i \]那麼我們考慮怎麼求\(f_i\)
我們有
\[\sum_{n}f(n)\frac{x^n}{n!} \]\[=\sum_{i=0}^mf_i\sum_{n=i}\frac{x^n}{(n-i)!} \]\[=\sum_{i=0}^mf_ix^i\sum_{n=i}\frac{1}{(n-i)!} \]\[=(\sum_{i=0}^mf_ix^i)e^x \]那麼我們的\(f_i\)就是\(\sum_{n}f(n)\frac{x^n}{n!}\)與\(e^x\)的卷積了
直接大力\(NTT\)即可,複雜度\(O(nlogn)\)
\(\mathcal{Code}\)
// Author: Ame__
#include<bits/stdc++.h>
#define _ 0
#define qwq double
#define AME__DEBUG
#define LL long long
#define bomb exit(0)
#define LOG(FMT...) fprintf(stderr , FMT)
#define TOWA(FMT...) fprintf(stdout , FMT)
using namespace std;
/*Grievous Lady*/
const int BUF_SIZE = 1 << 12;
char buf[BUF_SIZE] , *buf_s = buf , *buf_t = buf + 1;
#define PTR_NEXT() \
{ \
buf_s ++; \
if(buf_s == buf_t) \
{ \
buf_s = buf; \
buf_t = buf + fread(buf , 1 , BUF_SIZE , stdin); \
} \
}
#define mians(_s_) \
{ \
while(!isgraph(*buf_s)) PTR_NEXT();\
char register *_ptr_ = (_s_); \
while(isgraph(*buf_s) || *buf_s == '-') \
{ \
*(_ptr_ ++) = *buf_s; \
PTR_NEXT(); \
} \
(*_ptr_) = '\0'; \
}
template <typename _n_> void mian(_n_ & _x_){
while(*buf_s != '-' && !isdigit(*buf_s)) PTR_NEXT();
bool register _nega_ = false; if(*buf_s == '-'){ _nega_ = true; PTR_NEXT(); }
_x_ = 0; while(isdigit(*buf_s)){ _x_ = _x_ * 10 + *buf_s - '0'; PTR_NEXT(); } if(_nega_) _x_ = -_x_;
}
#define mod 998244353
const int kato = 4e6 + 10;
template <typename _n_> bool cmax(_n_ &a , const _n_ &b){ return a < b ? a = b , 1 : 0; }
template <typename _n_> bool cmin(_n_ &a , const _n_ &b){ return a > b ? a = b , 1 : 0; }
int n , m , x , qaq , ans , len = 1;
int A[kato] , B[kato] , fac[kato] , inv[kato] , fac_inv[kato];
inline int quick_pow(int a , int b){
int res = 1;
for(; b ; b >>= 1 , a = 1LL * a * a % mod){
if(b & 1){
res = 1LL * res * a % mod;
}
}
return res;
}
inline void NTT(int *y , int len , int opt){
int *rev = new int[len];
rev[0] = 0;
for(int i = 1;i < len;i ++) rev[i] = (rev[i >> 1] >> 1) | ((i & 1) * (len >> 1));
for(int i = 0;i < len;i ++) if(rev[i] > i) swap(y[rev[i]] , y[i]);
for(int i = 1;i < len;i <<= 1){
int G1 = quick_pow(3 , (mod - 1) / (i << 1));
for(int j = 0;j < len;j += (i << 1)){
for(int k = 0 , g = 1;k < i;k ++ , g = 1LL * g * G1 % mod){
int res = 1LL * y[i + j + k] * g % mod;
y[i + j + k] = ((y[j + k] - res) % mod + mod) % mod;
y[j + k] = (y[j + k] + res) % mod;
}
}
}
if(opt == -1){
reverse(y + 1 , y + len);
for(int i = 0 , inv = quick_pow(len , mod - 2);i < len;i ++) y[i] = 1LL * y[i] * inv % mod;
}
delete []rev; rev = 0x0;
}
#define init() \
{ \
fac[0] = fac[1] = inv[0] = inv[1] = fac_inv[0] = fac_inv[1] = 1; \
for(int i = 2;i <= m;i ++) \
{ \
fac[i] = 1LL * fac[i - 1] * i % mod; \
inv[i] = 1LL * (mod - mod / i) * inv[mod % i] % mod; \
fac_inv[i] = 1LL * fac_inv[i - 1] * inv[i] % mod; \
} \
}
inline int Ame_(){
#ifdef AME__
freopen(".in" , "r" , stdin); freopen(".out" , "w" , stdout); int nol_cl = clock();
#endif
mian(n) , mian(m) , mian(x); m ++; init();
for(int i = 0;i < m;i ++){
mian(qaq);
A[i] = 1LL * qaq * fac_inv[i] % mod;
B[i] = (i & 1) ? -fac_inv[i] : fac_inv[i];
}
for(len = 1 ; len <= 2 * m ; len <<= 1);
NTT(A , len , 1); NTT(B , len , 1);
for(int i = 0;i < len;i ++) A[i] = 1LL * A[i] * B[i] % mod;
NTT(A , len , -1);
int res = 1;
for(int i = 0;i < m;i ++){
ans = (ans + 1LL * A[i] * res % mod) % mod;
res = ((1LL * res * x % mod * (n - i) % mod) + mod) % mod;
}
TOWA("%d\n" , ans);
#ifdef AME__TIME
LOG("Time: %dms\n", int((clock() - nol_cl) / (qwq)CLOCKS_PER_SEC * 1000));
#endif
return ~~(0^_^0); /*さようならプログラム*/
}
int Ame__ = Ame_();
int main(){;}