以下把值域(题面里的\(lim\))记做\(m\)。
考虑求\(k\)的答案。考虑每个位置对答案的贡献,枚举位置\(i\),再枚举\(a[i]\)的值\(x\)。设:
\[
F(k)=\sum_{i=k+1}^{n}m^{i-k-1}m^{n-i}{i-1\choose k}\sum_{x=1}^{m}(x-1)^k
\]
如果对着式子打一个\(O(n^2m)\)的暴力,就会发现这个\(F(k)\)并不是答案,它比答案大。它其实是\(i\)前面至少有\(k\)个数的方案数。而一个序列,如果\(i\)前面有超过\(k\)个\(\leq x\)的数,比方说有\(j\)个,那这个序列就会被计算\(j\choose k\)次。形式化地说,如果设\(i\)前面恰好有\(k\)个\(\leq x\)数的方案数为\(G(k)\),则:
\[
F(k)=\sum_{i=k}^{n}{i\choose k}G(i)
\]
而这个\(G(k)\)的后缀和,就是我们要求的答案。
根据二项式反演,可知:
\[
G(k)=\sum_{i=k}^{n}(-1)^{i-k}{i\choose k}F(i)
\]
如果知道了\(F[1\dots n]\),就可以用NTT快速求出\(G[1\dots n]\),进而求出答案。这部分我们稍后再说,先看如何求\(F\)。
设\(S(m,k)=\sum_{i=0}^{m-1}i^k\)。则:
\[
\begin{align}
F(k)=&m^{n-k-1}S(m,k)\sum_{i=k+1}^{n}{i-1\choose k}\\
=&m^{n-k-1}S(m,k)\sum_{i=k}^{n-1}{i\choose k}\\
=&m^{n-k-1}S(m,k){n\choose k+1}
\end{align}
\]
最后一步相当于求杨辉三角上连续若干行,每行的第\(k\)个数的和(画出来是一条斜向左下的斜线)。可以从上到下把每两个数合并为下一行的第\(k+1\)个数。因此\(\sum_{i=k}^{n-1}{i\choose k}={n\choose k+1}\)。
现在的难点是求出对所有\(k\in[1,n]\),求出\(S(m,k)\)。构造\(S(m,k)\)的指数生成函数\(H(x)=\sum_{i=0}^{\infty}S(m,i)\frac{x^i}{i!}\),则:
\[
\begin{align}
H(x)&=\sum_{i=0}^{\infty}\frac{x^i}{i!}\sum_{j=0}^{m-1}j^i\\
&=\sum_{j=0}^{m-1}\sum_{i=0}^{\infty}\frac{x^ij^i}{i!}
\end{align}
\]
根据小学二年级学过的泰勒展开,我们知道,\(e^x=\sum_{i=0}^{\infty}\frac{x^i}{i!}\)。把这里的\(x\)替换为\((jx)\),则:
\[
\begin{align}
H(x)&=\sum_{j=0}^{m-1}e^{jx}\\
&=\frac{e^{mx}-1}{e^x-1}
\end{align}
\]
注:这一步变换是等比数列求和,即把式子整体乘以\(e^x\)再用新式子减去原式。
再次感谢泰勒展开,我们知道分子和分母其实分别是两个无限长的数列。我们要求是\(H(x)\)的前\(n\)项的值。这个值显然只和两个数列的前\(n\)项有关,因此取出分母的前\(n\)项,做多项式求逆,再用NTT把两个数列乘起来即可。分母的常数项是\(0\)不好处理,但注意到分子的常数项也是\(0\),我们只要把分子分母同时除以\(x\)就行,即两个数列分别左移一位就行。
这样,我们就求出了\(F[0\dots n-1]\),回顾开头部分的讨论,我们要求出:\(G(k)=\sum_{i=k}^{n-1}(-1)^{i-k}{i\choose k}F(i)\quad k\in[0,n-1]\)。
把组合数拆开,得到:
\[
G(k)=\sum_{i=k}^{n-1}(-1)^{i-k}\frac{i!}{k!(i-k)!}F(i)
\]
先把\(k!\)拎出来。然后设\(f_i=i!F(i),g_i=(-1)^i\frac{1}{i!}\),则:
\[
G(k)=k!\sum_{i=k}^{n-1}f_ig_{i-k}
\]
我们熟悉的卷积都是\(c_k=\sum_{i=0}^{k}a_ib_{k-i}\),而这里的形式是\(c_k=\sum_{i=k}^{n}f_ig_{i-k}\),考虑如何转化。注:这里为方便表述,我们令\(n=n-1\)。
仔细考虑卷积的本质,我们的目标是要让相乘的两个数下标之和为定值,而我们能做的事就是尝试翻转一个数列。不妨尝试翻转\(f\),即令\(f_i=f_{n-i}\)。
此时\(c_k=\sum_{i=k}^{n}f_{n-i}g_{i-k}\)。
把\(i\)换成\(i-k\),则\(c_k=\sum_{i=0}^{n-k}f_{n-k-i}g_{i}\)。发现\(f\)和\(g\)的下标和为\(n-k\),刚好是一个定值。此时如果把\(f\)和\(g\)卷起来,那么结果的第\(n-k\)项就是\(c_k\)。再乘上\(k!\)就是\(G(k)\)了。
求出\(G\)后,做后缀和,得到答案。
时间复杂度\(O(n\log n)\),共做了一次多项式求逆,两次多项式乘法。
参考代码:
//problem:nflsoj550 #include <bits/stdc++.h> using namespace std; #define pb push_back #define mk make_pair #define lob lower_bound #define upb upper_bound #define fst first #define scd second typedef unsigned int uint; typedef long long ll; typedef unsigned long long ull; typedef pair<int,int> pii; /* ------ by:duyi ------ */ // dysyn1314 const int MAXN=114514,MOD=998244353; inline int mod1(int x){return x<MOD?x:x-MOD;} inline int mod2(int x){return x<0?x+MOD:x;} inline void add(int &x,int y){x=mod1(x+y);} inline void sub(int &x,int y){x=mod2(x-y);} inline int pow_mod(int x,int i){int y=1;while(i){if(i&1)y=(ll)y*x%MOD;x=(ll)x*x%MOD;i>>=1;}return y;} int n,m,fac[MAXN+5],invf[MAXN+5]; void init(){ fac[0]=1; for(int i=1;i<=MAXN;++i)fac[i]=(ll)fac[i-1]*i%MOD; invf[MAXN]=pow_mod(fac[MAXN],MOD-2); for(int i=MAXN-1;i>=0;--i)invf[i]=(ll)invf[i+1]*(i+1)%MOD; } inline int comb(int n,int k){ if(n<k)return 0; return (ll)fac[n]*invf[k]%MOD*invf[n-k]%MOD; } int f[MAXN*4+5],g[MAXN*4+5],h[MAXN*4+5],rev[MAXN*4+5]; void NTT(int *a,int n,int flag){ for(int i=0;i<n;++i)if(i<rev[i])swap(a[i],a[rev[i]]); for(int i=1;i<n;i<<=1){ int T=pow_mod(3,(MOD-1)/(i<<1)); if(flag==-1) T=pow_mod(T,MOD-2); for(int j=0;j<n;j+=(i<<1)){ for(int k=0,t=1;k<i;++k,t=(ll)t*T%MOD){ int Nx=a[j+k],Ny=(ll)a[i+j+k]*t%MOD; a[j+k]=mod1(Nx+Ny); a[i+j+k]=mod2(Nx-Ny); } } } if(flag==-1){ int invn=pow_mod(n,MOD-2); for(int i=0;i<n;++i)a[i]=(ll)a[i]*invn%MOD; } } void mul(int *a,int *b,int n,int m){ // b <- a*b static int f[MAXN*4+5],g[MAXN*4+5]; for(int i=0;i<n;++i)f[i]=a[i]; for(int i=0;i<m;++i)g[i]=b[i]; int lim=1,ct=0; while(lim<=n+m)lim<<=1,ct++; for(int i=n;i<lim;++i)f[i]=0; for(int i=m;i<lim;++i)g[i]=0; for(int i=0;i<lim;++i)rev[i]=(rev[i>>1]>>1)|((i&1)<<(ct-1)); NTT(f,lim,1); NTT(g,lim,1); for(int i=0;i<lim;++i)f[i]=(ll)f[i]*g[i]%MOD; NTT(f,lim,-1); for(int i=0;i<n;++i)b[i]=f[i]; } void _mul(int *a,int *b,int n,int m){ // b <- 2b-a*b^2 static int f[MAXN*4+5],g[MAXN*4+5]; for(int i=0;i<n;++i)f[i]=a[i]; for(int i=0;i<m;++i)g[i]=b[i]; int lim=1,ct=0; while(lim<=(n*2))lim<<=1,ct++; for(int i=n;i<lim;++i)f[i]=0; for(int i=m;i<lim;++i)g[i]=0; for(int i=0;i<lim;++i)rev[i]=(rev[i>>1]>>1)|((i&1)<<(ct-1)); NTT(f,lim,1); NTT(g,lim,1); for(int i=0;i<lim;++i)f[i]=mod2(mod1(g[i]*2)-(ll)f[i]*g[i]%MOD*g[i]%MOD); NTT(f,lim,-1); for(int i=0;i<n;++i)b[i]=f[i]; } void get_inv(int *a,int n,int *res){ if(n==1){ res[0]=pow_mod(a[0],MOD-2); return; } int m=(n+1)>>1; get_inv(a,m,res); _mul(a,res,n,m); } int main() { init(); scanf("%d%d",&n,&m);m%=MOD; for(int i=0,pw=1;i<n;++i){ pw=(ll)pw*m%MOD; f[i]=(ll)pw*invf[i+1]%MOD; g[i]=invf[i+1]; } get_inv(g,n,h);//h=g^{-1} mul(f,h,n,n);//h=f*h for(int i=0;i<n;++i)h[i]=(ll)h[i]*fac[i]%MOD; //for(int i=0;i<n;++i)cout<<h[i]<<" ";cout<<endl; memset(f,0,sizeof(f)); memset(g,0,sizeof(g)); for(int k=0;k<n;++k)f[k]=(ll)pow_mod(m,n-k-1)*h[k]%MOD*comb(n,k+1)%MOD;//f[k]至少选k个的贡献 //f[k] -> g[k](恰好选k个的贡献) for(int i=0;i<n;++i)f[i]=(ll)f[i]*fac[i]%MOD; for(int i=0;i<n;++i)h[i]=f[n-1-i]; for(int i=0;i<n;++i)f[i]=h[i]; for(int i=0;i<n;++i)if(i&1)g[i]=mod2(-invf[i]);else g[i]=invf[i]; mul(f,g,n,n); for(int i=0;i<n;++i)h[i]=g[n-1-i]; for(int i=0;i<n;++i)g[i]=(ll)h[i]*invf[i]%MOD;g[n]=0; for(int i=n-1;i>=1;--i)add(g[i],g[i+1]); for(int i=1;i<=n;++i)printf("%d ",g[i]);puts(""); return 0; }
来源:https://www.cnblogs.com/dysyn1314/p/12371957.html