CF1278F Cards

CF1278F Cards

首先我们知道,一次拿牌的概率是$P(i) = \frac 1 m$,同时权值是1,所以期望就是$\frac{1} m$,拿$n$次牌贡献是独立的,就是$\frac n m$。

但是我们要算的是$k$次方的期望,众所周知期望的二次方不等于二次方的期望。我们考虑$E$的意义,$E$在这一次拿到 Joker 的$\frac 1 m$的概率下是 1 ,其他情况是0。则$E^2$就是随机两次,这两次都是 1 的情况下是 1 ,其他情况是0。

我们把这$n$次是否抓到 Joker 的 0/1 写成一个序列,所以知道最后统计的答案,就是所有的长度为$k$的有序子序列(可以是$A_3,A_2,A_2$这种的 ),它做出贡献的前提就是这个子序列的所有随机变量都去到 1 了。

接着考虑,如果两个序列的位置种类数一致,那么它们出现的概率是相同的。如果知道这些位置都是 Joker ,那么这些位置组成的所有序列都会出现。

所以考虑一个 dp ,$dp[i][j]$表示当前在选择第$i$个位置,到达这个位置时已经有$j$个不同的位置出现了。那么$\sum dp[k][i] \times \frac{1}{m^{i}}$就是答案,因为有$\frac{1}{m^i}$的概率这$i$个钦定的元素位置都是 Joker,这样带来的权值就是方案数。然后考虑这个 dp 的递推,这是很轻松的:

就是考虑第$i$个位置是选择前$j$个之一还是新选择一种。

代码很简单:

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
#include "algorithm"
#include "iostream"
#include "cstring"
#include "cstdio"
using namespace std;
#define MAXN 5006
#define P 998244353
int n , m , k;
int dp[MAXN][MAXN];
int Pow( int a , int b ) {
int cur = a % P , ans = 1;
while( b ) {
if( b & 1 ) ans = 1ll * ans * cur % P;
cur = 1ll * cur * cur % P , b >>= 1;
}
return ans;
}
int main( ) {
cin >> n >> m >> k;
dp[0][0] = 1;
for( int i = 1 ; i <= k ; ++ i ) {
for (int j = 1; j <= i; ++j)
dp[i][j] = ( 1ll * dp[i-1][j] * j % P + 1ll * dp[i-1][j-1] * ( n - j + 1 ) % P ) % P;
}
int res = 0 , cur = 1 , p = Pow( m , P - 2 );
for( int i = 0 ; i <= k ; ++ i )
( res += 1ll * dp[k][i] * cur % P ) %= P , cur = 1ll * cur * p % P;
cout << res << endl;
}
文章作者: yijan
文章链接: https://yijan.co/old57/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Yijan's Blog