【CF891E】Lust(生成函数)

点此看题面

  • 给定一个长度为(n)的序列(a),进行(k)次操作。
  • 每次操作随机选中一个(i),将(a_i)(1),收益为除它以外所有数的乘积,求期望收益。
  • (nle5 imes10^3,kle10^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 imes (a_i-p) imes f_{i-1,q} ]

经典暴拆组合数:

[frac{f_{i,p+q}}{(p+q)!}=sumfrac{a_i-p}{p!} imesfrac{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_{1sim n}(x))(n)个生成函数卷起来就能得到(F_n(x)),而它的(k)次项系数就是(frac{f_{n,k}}{k!})了。

推式子

对于(G_i(x)),我们把(a_i-p)分开来:

[G_i(x)=a_isum_{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_{1sim n}(x))(n)个生成函数卷起来之后再卷上(e^{nx})

很容易(O(n^2))暴力求出(A_{1sim 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 imesfrac{n^{k-i}}{(k-i)!} ]

乘上一个(k!)得到了总和(f_{n,k}),再除以总方案数(n^k)得到期望:

[E=sum_{i=0}^{k}frac{f_i imes 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
",(s-t+X)%X),0;//原乘积-修改后乘积期望值
}
败得义无反顾,弱得一无是处
原文地址:https://www.cnblogs.com/chenxiaoran666/p/CF891E.html