0%

Codeforces 891E Lust

题目大意

给定一个有 \(n\) 个整数的序列 \({a_n}\),有一个初始为 \(0\) 的值 \(res\),重复下面的过程 \(k\) 次:

随机选择一个 \([1,n]\) 之间的下标 \(x\)\(res\) 加上所有满足 \(i\neq x\)\(a_i\) 的乘积,然后将 \(a_x\) 减去 \(1\)

求最后 \(res\) 的期望值,对 \(10^9+7\) 取模。

\(1\leq n\leq5\times10^3,1\leq k\leq10^9\)


题目分析

可以发现,每一次 \(res\) 加上的值其实就是操作之前的 \(a\) 的积减去操作之后的 \(a\) 的积。

将这一连串的求和相消,发现最后只剩下最开始的 \(a\) 的积减去最后 \(a\) 的积。

我们现在只要求最后的 \(a\) 的积的期望。

我们令 \(F_j(x)\) 表示序列第 \(j\) 个数对答案贡献的生成函数,显然有: \[ \begin{align} F_j(x)&=\sum_{i=0}^\infty \frac{(a_j-i)x^i}{i!}\\ &=(a_j-x)e^x \end{align} \]

那么答案的生成函数是

\[ \begin{align} F(x)&=\prod_{i=1}^n{F_i(x)}\\ &=e^{nx}\prod_{i=1}^n(a_i-x) \end{align} \]

\(e^{nx}\) 很好求,后面的直接 \(O(n^2)\) 的 dp 暴力展开就好了。

时间复杂度\(O(n^2)\)


代码实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
#include <iostream>
#include <cstdio>

using namespace std;

const int P=1000000007;
const int N=5005;

int f[N][N];
int a[N];
int n,k,ans,mul;

inline int calc(int st,int en)
{
int ret=1;
for (int i=st;i<=en;++i) ret=1ll*ret*i%P;
return ret;
}

inline int quick_power(int x,int y)
{
int ret=1;
for (;y;y>>=1,x=1ll*x*x%P) if (y&1) ret=1ll*ret*x%P;
return ret;
}

int main()
{
freopen("lust.in","r",stdin),freopen("lust.out","w",stdout);
scanf("%d%d",&n,&k),mul=1;
for (int i=1;i<=n;++i) scanf("%d",&a[i]),mul=1ll*mul*a[i]%P;
f[0][0]=1;
for (int i=1;i<=n;++i)
for (int j=0;j<=i;++j)
f[i][j]=(1ll*f[i-1][j]*a[i]%P+(j?P-f[i-1][j-1]:0))%P;
ans=0;
for (int i=0;i<=n&&i<=k;++i) (ans+=1ll*f[n][i]*quick_power(n,k-i)%P*calc(k-i+1,k)%P)%=P;
ans=(mul+P-1ll*ans*quick_power(quick_power(n,k),P-2)%P)%P;
printf("%d\n",ans);
fclose(stdin),fclose(stdout);
return 0;
}