题解 P6667 【[清华集训2016] 如何优雅地求和】

· · 题解

题面

给定 n,x 和多项式 F(x)0,1,\cdots,m 的点值 a_i,求

\sum\limits_{k=0}^{n}F(k)\binom{n}{k}x^{k}(1-x)^{n-k}

答案对 998244353 取模。

\texttt{Data Range:}n\leq 10^9,m\leq 2\times 10^4,0\leq a_i,x\lt 998244353

题解

目前是本题最优解。(虽然很快不是了)

先吐槽一句:这个毒瘤 \texttt{F\color{red}ee\_cle6418} 咋样例输出都能打错啊,严重影响做题体验。

不知道大家有没有做过 A 卷的组合数问题,没看过的可以先去看我的题解。麻烦了麻烦了给我点个赞捧个场吧

同样,我们还是要对这个式子进行降维打击。有了上一题的经验,我们可以直接考虑先拆下降幂。假设 F(x)=\sum\limits_{k=0}^{m}f_kx^{\underline k},那么原式可以写成

\sum\limits_{k=0}^{n}\binom{n}{k}x^{k}(1-x)^{n-k}\sum\limits_{i=0}^{\min(m,k)}f_i\frac{k!}{(k-i)!}

接下来来一波交换求和顺序,顺手拆掉二项式系数,约一下分:

\sum\limits_{i=0}^{m}\sum\limits_{k=i}^{n}x^{k}(1-x)^{n-k}f_i\frac{n!}{(n-k)!(k-i)!}

如果分子是 (n-i)! 那就更好了,于是可以考虑凑一个出来:

\sum\limits_{i=0}^{m}\sum\limits_{k=i}^{n}x^{k}(1-x)^{n-k}f_i\binom{n-i}{k-i}\frac{n!}{(n-i)!}

接下来有一个很绝妙的操作:二项式中有一个分母是 k-i,另一个是 n-k,而 1-x 的次数是 n-k,所以我们也想凑一个 x 的次数为 k-i(其实这个操作曾经出现在 P6073,但是那个题不仅拆了幂来凑形式也拆了二项式来凑卷积),于是提取一下与 k 无关的东西,并凑 x 的指数,得到

\sum\limits_{i=0}^{m}f_ix^i\frac{n!}{(n-i)!}\sum\limits_{k=i}^{n}x^{k-i}(1-x)^{n-k}\binom{n-i}{k-i}

接下来如果对推柿子有感觉的可以直接看出是二项式定理,如果没感觉的话可以做 k\gets k-i 的代换(就是新的 k 代表原来的 k-i),以及设 p=n-i

\sum\limits_{i=0}^{m}f_ix^i\frac{n!}{(n-i)!}\sum\limits_{k=0}^{n-i}x^{k}(1-x)^{p-k}\binom{p}{k}

所以右边的求和项就是 (x+1-x)^{p+1}=1,于是就得到最终式子

\sum\limits_{i=0}^{m}f_ix^i\frac{n!}{(n-i)!}

接下来考虑怎么求下降幂多项式的系数。

先考虑下降幂单项式如何转换成普通多项式,考虑 x^{\underline n}\texttt{EGF},设为 F_0(x)。(因为 k\geq n 的时候下降幂才有非零值)

F_0(x)=\sum\limits_{k=n}^{\infty}\frac{k^{\underline n}}{k!}x^k

根据定义式拆掉下降幂并提出 x^n约分得到

F_0(x)=x^n\sum\limits_{k=n}^{\infty}\frac{x^{k-n}}{(k-n)!}

k\gets k-n 的代换

F_0(x)=x^n\sum\limits_{k=0}^{\infty}\frac{x^k}{k!}

于是得到

F_0(x)=x^ne^x

接下来设 F(x) 为某下降幂多项式点值的 \texttt{EGF}G(x) 为其系数的 \texttt{OGF}(注意!这里不是 \texttt{EGF}!),f 为该多项式的系数序列,得到

F(x)=\sum\limits_{k=0}^{\infty}f_kx^{\underline k}=e^x\sum\limits_{k=0}^{\infty}f_kx^k=e^xG(x)

所以 G(x)=e^{-x}F(x)

题面中给我们的是 F(x),所以可以求出 G(x) 并得到答案。时间复杂度 O(m\log m)

### 代码 ```cpp #include<cstdio> #include<cstring> #include<cctype> #include<cmath> #include<algorithm> #pragma GCC optimize("Ofast,unroll-loops") using namespace std; typedef int ll; typedef long long int li; typedef unsigned long long int ull; const ll MAXN=65537,MOD=998244353,G=3,INVG=332748118; ll n,m,x,res,binom=1,pw=1; ll rev[MAXN],omgs[MAXN],invo[MAXN],f[MAXN],expn[MAXN],fact[20005],finv[20005]; inline ll read() { register ll num=0; register char ch=getchar(); while(!isdigit(ch)&&ch!='-') { ch=getchar(); } while(isdigit(ch)) { num=(num<<3)+(num<<1)+(ch-'0'); ch=getchar(); } return num; } inline ll qpow(ll base,ll exponent) { ll res=1; while(exponent) { if(exponent&1) { res=(li)res*base%MOD; } base=(li)base*base%MOD,exponent>>=1; } return res; } inline void setup(ll cnt) { fact[0]=fact[1]=finv[0]=1; for(register int i=1;i<=cnt;i++) { fact[i]=(li)fact[i-1]*i%MOD; } finv[cnt]=qpow(fact[cnt],MOD-2); for(register int i=cnt-1;i;i--) { finv[i]=(li)finv[i+1]*(i+1)%MOD; } } inline void setupOmg(ll cnt) { ll limit=log2(cnt)-1,omg; omg=qpow(G,(MOD-1)>>(limit+1)),omgs[cnt>>1]=1; for(register int i=(cnt>>1|1);i!=cnt;i++) { omgs[i]=(li)omgs[i-1]*omg%MOD; } for(register int i=(cnt>>1)-1;i;i--) { omgs[i]=omgs[i<<1]; } } inline void NTT(ll *cp,ll cnt,ll inv) { static ull tcp[MAXN]; register ll cur=0,x,shift=log2(cnt)-__builtin_ctz(cnt); if(inv==-1) { reverse(cp+1,cp+cnt); } for(register int i=0;i<cnt;i++) { tcp[rev[i]>>shift]=cp[i]; } for(register int i=2;i<=cnt;i<<=1) { cur=i>>1; for(register int j=0;j<cnt;j+=i) { for(register int k=0;k<cur;k++) { x=tcp[j|k|cur]*omgs[k|cur]%MOD; tcp[j|k|cur]=tcp[j|k]+MOD-x,tcp[j|k]+=x; } } } for(register int i=0;i<cnt;i++) { cp[i]=tcp[i]%MOD; } if(inv==1) { return; } x=MOD-(MOD-1)/cnt; for(register int i=0;i<cnt;i++) { cp[i]=(li)cp[i]*x%MOD; } } inline void conv(ll fd,ll *f,ll *g,ll *res) { static ll tmpf[MAXN],tmpg[MAXN]; ll cnt=1,limit=-1; while(cnt<(fd<<1)) { cnt<<=1,limit++; } memcpy(tmpf,f,fd<<2),memcpy(tmpg,g,fd<<2); for(register int i=0;i<cnt;i++) { rev[i]=(rev[i>>1]>>1)|((i&1)<<limit); } NTT(tmpf,cnt,1),NTT(tmpg,cnt,1); for(register int i=0;i<cnt;i++) { tmpf[i]=(li)tmpf[i]*tmpg[i]%MOD; } NTT(tmpf,cnt,-1),memcpy(res,tmpf,fd<<2); } int main() { n=read(),m=read(),x=read(),setupOmg(65536),setup(m+1); for(register int i=0;i<=m;i++) { f[i]=(li)read()*finv[i]%MOD,expn[i]=i&1?MOD-finv[i]:finv[i]; } conv(m+1,f,expn,f); for(register int i=0;i<=m;i++) { res=(res+(li)f[i]*binom%MOD*pw)%MOD; binom=(li)binom*(n-i)%MOD,pw=(li)pw*x%MOD; } printf("%d\n",res); } ```