【CF891E】Lust(生成函数)
- 给定一个长度为\(n\)的序列\(a\),进行\(k\)次操作。
- 每次操作随机选中一个\(i\),将\(a_i\)减\(1\),收益为除它以外所有数的乘积,求期望收益。
- \(n\le5\times10^3,k\le10^9\)
重要转化
将\(a_i\)减\(1\)后,除它以外所有数的乘积恰好是\(\prod_{i=1}^na_i\)的变化量。
所以说,答案实际上就是原本的\(\prod_{i=1}^na_i\)减去修改后\(\prod_{i=1}^na_i'\)的期望值。
从动态规划到生成函数
考虑暴力\(DP\),设\(f_{i,j}\)表示前\(i\)个数一共修改了\(j\)次的所有方案下乘积之和。
转移就是枚举当前位置修改了多少次,得到:
\[f_{i,p+q}=\sum C_{p+q}^p\times (a_i-p)\times f_{i-1,q}
\]
经典暴拆组合数:
\[\frac{f_{i,p+q}}{(p+q)!}=\sum\frac{a_i-p}{p!}\times\frac{f_{i-1,q}}{q!}
\]
设\(F_i(x)=\sum_{p=0}^{+\infty}f_{i,p}\frac{x^p}{p!},G_i(x)=\sum_{p=0}^{+\infty}(a_i-p)\frac{x^p}{p!}\),得到:
\[F_i(x)=F_{i-1}(x)*G_i(x)
\]
因此只要把\(G_{1\sim n}(x)\)这\(n\)个生成函数卷起来就能得到\(F_n(x)\),而它的\(k\)次项系数就是\(\frac{f_{n,k}}{k!}\)了。
推式子
对于\(G_i(x)\),我们把\(a_i-p\)分开来:
\[G_i(x)=a_i\sum_{p=0}^{+\infty}\frac{x^p}{p!}-\sum_{p=0}^{+\infty}\frac{x^{p+1}}{p!}=(a_i-x)e^x
\]
设\(A_i(x)=a_i-x\),发现\(F_n(x)\)就是\(A_{1\sim n}(x)\)这\(n\)个生成函数卷起来之后再卷上\(e^{nx}\)。
很容易\(O(n^2)\)暴力求出\(A_{1\sim n}(x)\)卷起来后每一项的系数\(f_i\),于是:
\[F_n(x)=(\sum_{i=0}^{+\infty}f_ix^i)*(\sum_{i=0}^{+\infty}\frac {(nx)^i}{i!})\\
[x^k]F_n(x)=\sum_{i=0}^kf_i\times\frac{n^{k-i}}{(k-i)!}
\]
乘上一个\(k!\)得到了总和\(f_{n,k}\),再除以总方案数\(n^k\)得到期望:
\[E=\sum_{i=0}^{k}\frac{f_i\times k^{\underline i}}{n^i}
\]
最终答案就是\(\prod_{i=1}^na_i-E\)。
代码:\(O(n^2)\)
#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 5000
#define X 1000000007
using namespace std;
int n,k,a[N+5],f[N+5];
I int QP(RI x,RI y) {RI t=1;W(y) y&1&&(t=1LL*t*x%X),x=1LL*x*x%X,y>>=1;return t;}
int main()
{
RI i,j;for(scanf("%d%d",&n,&k),f[0]=i=1;i<=n;++i) for(scanf("%d",a+i),j=i;~j;--j) f[j]=(1LL*a[i]*f[j]+(j?X-f[j-1]:0))%X;//暴力DP求系数
RI t=0,Iv=QP(n,X-2),g=1,p=1;for(i=0;i<=min(n,k);++i) t=(t+1LL*f[i]*g%X*p)%X,g=1LL*g*(k-i)%X,p=1LL*p*Iv%X;//计算答案
RI s=1;for(i=1;i<=n;++i) s=1LL*s*a[i]%X;return printf("%d\n",(s-t+X)%X),0;//原乘积-修改后乘积期望值
}
待到再迷茫时回头望,所有脚印会发出光芒