题意
现在给你一有n个整数的序列a[],有一个初始为0的值res,重复下面的过程k次:
“随机选择一个[1,n]之间的下标x,res加上所有满足i≠x的a[i]的乘积,然后将a[x]减去1”
问最后res的期望值,对10^9+7取模
n<=5000
k<=10^9
分析
这个其实就是要求
∏A−∏A′
∏
A
−
∏
A
′
其中A表示初始状态的乘积,A’表示结束状态乘积的期望。
那么现在问题就是我们怎么求A’
我们设我们对 a[i] a [ i ] 操作了 b[i] b [ i ] 次,那么我们有
E=k!∏(a[i]−b[i])nk∏(b[i])!
E
=
k
!
∏
(
a
[
i
]
−
b
[
i
]
)
n
k
∏
(
b
[
i
]
)
!
我们考虑第i个的生成函数位
fi=∑j≥0ai−jj!
f
i
=
∑
j
≥
0
a
i
−
j
j
!
于是总的生成函数就为
f(x)=∏∑j≥0ai−jj!xj
f
(
x
)
=
∏
∑
j
≥
0
a
i
−
j
j
!
x
j
=∏∑aixjj!−x∗xj−1(j−1)!
=
∏
∑
a
i
x
j
j
!
−
x
∗
x
j
−
1
(
j
−
1
)
!
=enx∏(ai−x)
=
e
n
x
∏
(
a
i
−
x
)
后面的我们就可以n^2处理了
代码
#include <bits/stdc++.h>
const int N = 5000 + 10;
const int MOD = 998244353;
int read()
{
int x = 0, f = 1;
char ch = getchar();
while (ch < '0' || ch > '9') {if (ch == '-') f = -1; ch = getchar();}
while (ch >= '0' && ch <= '9') {x = x * 10 + ch - '0'; ch = getchar();}
return x * f;
}
int f[N][N];
int a[N];
int ksm(int x,int y)
{
int res = 1;
while (y)
{
if (y & 1)
res = 1ll * x * res % MOD;
x = 1ll * x * x % MOD;
y >>= 1;
}
return res;
}
int main()
{
freopen("manastorm.in","r",stdin);
freopen("manastorm.out","w",stdout);
int n = read(), k = read();
for (int i = 1; i <= n; i++)
a[i] = read();
f[0][0] = 1;
for (int i = 1; i <= n; i++)
{
f[i][0] = 1ll * f[i - 1][0] * a[i] % MOD;
for (int j = 1; j <= i; j++)
f[i][j] = (1ll * f[i - 1][j] * a[i] - f[i - 1][j - 1]) % MOD;
}
int ans = 0;
for (int i = 0; i <= std::min(n, k); i++)
{
int res = 1;
for (int j = k - i + 1; j <= k; j++)
res = 1ll * res * j % MOD;
ans = (ans + 1ll * f[n][i] * ksm(n, k - i) % MOD * res) % MOD;
}
ans = 1ll * ans * ksm(ksm(n, k), MOD - 2) % MOD;
int m = 1;
for (int i = 1; i <= n; i++)
m = 1ll * m * a[i] % MOD;
m = (m - ans + MOD) % MOD;
printf("%d\n",m);
}
本文介绍了一种计算特定随机序列操作后结果期望值的方法,通过生成函数和组合数学技巧,有效地解决了大规模数据集上的计算问题。
407

被折叠的 条评论
为什么被折叠?



