- 给定一个长度为(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;//原乘积-修改后乘积期望值
}