题意
思路
与其说是一道概率dp,不如说是一道组合数学。
我们考虑计算一个状态 S S S的贡献,它被涂黑的行列数记为 f ( S ) = x + y f(S)=x+y f(S)=x+y,则对分数的贡献为 2 f ( S ) = 2 x + y = 2 x ∗ 2 y 2^{f(S)} =2^{x+y}=2^x*2^y 2f(S)=2x+y=2x∗2y,所以 S S S状态的分数贡献就是 x x x行 y y y列的子集数量。
设
S
S
S状态的集合为
M
M
M,则
S
S
S的得分期望值为
1
∣
M
∣
∑
S
′
∈
S
2
f
(
S
′
)
=
1
∣
M
∣
∑
S
′
∈
S
∑
i
=
0
n
∑
j
=
0
n
\frac{1}{|M|}\sum_{S'\in S}2^{f(S')}=\frac{1}{|M|}\sum_{S'\in S}\sum_{i=0}^{n}\sum_{j=0}^{n}
∣M∣1∑S′∈S2f(S′)=∣M∣1∑S′∈S∑i=0n∑j=0n(从
S
′
S'
S′被完全标记的行列中选取
i
i
i行
j
j
j列的方案数)
设 P x , y P_{x,y} Px,y为选出 x x x行 y y y列被涂黑的概率,则状态 S ′ S' S′选出 x x x行 y y y列的方案数为 P x , y ∗ C n x C n y P_{x,y}*C_{n}^{x}C_{n}^{y} Px,y∗CnxCny,那么总的方案数就为 ∑ x = 0 n ∑ y = 0 n P x , y ∗ C n x C n y \sum_{x=0}^{n}\sum_{y=0}^{n}P_{x,y}*C_{n}^{x}C_{n}^{y} ∑x=0n∑y=0nPx,y∗CnxCny
下面考虑如何计算 P x , y P_{x,y} Px,y。 m m m中选 k k k,总共有 C m k C_{m}^{k} Cmk种方案。容易知道,设 N u m = x n + y n − x y Num=xn+yn-xy Num=xn+yn−xy,就是这 x x x行 y y y列的总格子数,因为要被涂黑,所以这 N u m Num Num个数一定包含在选取的 k k k个数中。而其他的 k − N u m k-Num k−Num个数则随意选取,方案数为 C m − N u m k − N u m C_{m-Num}^{k-Num} Cm−Numk−Num,所以 P x , y = C m − N u m k − N u m C m k P_{x,y}=\frac{C_{m-Num}^{k-Num}}{C_{m}^{k}} Px,y=CmkCm−Numk−Num。然而会发现,剩下的涂了之后会导致行、列数大于 x x x或 y y y,我们只需要改变 P x , y P_{x,y} Px,y的含义为至少选出 x x x行 y y y列被涂黑的概率即可。
至此为止,公式就推出来了,可以套公式直接算,预处理组合数的话复杂度为 O ( M a x ( n 2 , m ) ) O(Max(n^2,m)) O(Max(n2,m)),然而这道题是没有模数的,意味着阶乘算组合数时会爆炸,我们就需要一些方法计算组合数。
记
l
g
[
i
]
=
l
o
g
(
i
!
)
lg[i]=log(i!)
lg[i]=log(i!)(这是一些同学最喜欢的数组),得到
l
g
[
i
]
=
l
o
g
(
i
!
)
=
l
o
g
(
i
∗
(
i
−
1
)
!
)
=
l
g
[
i
−
1
]
+
l
o
g
(
i
)
lg[i]=log(i!)=log(i*(i-1)!)=lg[i-1]+log(i)
lg[i]=log(i!)=log(i∗(i−1)!)=lg[i−1]+log(i),这里大家学多项式的时候应该都知道了。我们知道,
C
n
m
=
J
C
[
n
]
/
J
C
[
m
]
/
J
C
[
n
−
m
]
C_{n}^{m}=JC[n]/JC[m]/JC[n-m]
Cnm=JC[n]/JC[m]/JC[n−m],通过
l
g
lg
lg数组,我们可以将阶乘的乘除转换为指数的加减,由此可以得到:
C
n
m
=
e
x
p
(
l
g
[
n
]
−
l
g
[
m
]
−
l
g
[
n
−
m
]
)
C_{n}^{m}=exp(lg[n]-lg[m]-lg[n-m])
Cnm=exp(lg[n]−lg[m]−lg[n−m])。这道题数据太大了,要使用long double,输出时转为double即可。
Code:
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#define MAXN 100005
#define Mod 1000000007
#define LL long long
#define DB double
#define LD long double
#define Int register int
using namespace std;
inline void read(LL &x)
{
x = 0;
LL f = 1;
char s = getchar();
while (s < '0' || s > '9')
{
if (s == '-')
f = -1;
s = getchar();
}
while (s >= '0' && s <= '9')
{
x = (x << 3) + (x << 1) + (s ^ 48);
s = getchar();
}
x *= f;
}
inline LL QuickPow(LL x,LL y)
{
LL Res = 1, Temp = x;
while ( y )
{
if (y & 1)
Res = Res * Temp % Mod;
Temp = Temp * Temp % Mod;
y /= 2;
}
return Res;
}
LD lg[MAXN], Ans;
inline LD C(LL n,LL m)
{
return lg[n] - lg[m] - lg[n - m];
}
int main()
{
LL n, m, k;
read( n ); read( m ); read( k );
for (Int i = 2; i <= m; ++ i)
lg[i] = lg[i - 1] + log( i );
for (Int r = 0; r <= n; ++ r)
for (Int c = 0; c <= n; ++ c)
{
LL Num = (r + c) * n - r * c;
if (Num <= k)
Ans += exp(C(n, r) + C(n, c) + C(m - Num, k - Num) - C(m, k));
}
if (Ans < 1e99)
printf("%.10lf", (DB)Ans);
else printf("1e99");
return 0;
}