题目描述:
题目分析:
确定恰好选
r
r
r行,
c
c
c列并不好做。
而
2
t
2^t
2t可以看作所选行列的所有子集个数,所以可以通过计算至少选
r
r
r行,
c
c
c列的方案数来统计答案。
记
P
(
r
,
c
)
P(r,c)
P(r,c)为至少选
r
r
r行,
c
c
c列的概率,那么答案
=
C
n
r
∗
C
n
c
∗
P
(
r
,
c
)
=C_n^r* C_n^c* P(r,c)
=Cnr∗Cnc∗P(r,c)
记选
r
r
r行,
c
c
c列的不重复元素个数为
x
=
(
r
+
c
)
∗
n
−
r
∗
c
x=(r+c)*n-r*c
x=(r+c)∗n−r∗c。
那么
P
(
r
,
c
)
=
A
m
n
2
C
m
−
z
k
−
z
A
m
n
2
C
m
k
P(r,c)=\frac {A_m^{n^2}C_{m-z}^{k-z}}{A_m^{n^2}C_m^k}
P(r,c)=Amn2CmkAmn2Cm−zk−z
分子相当于已经定下r行c列中要选哪些数,剩下的数随便选的方案数。
此题没有模数,用double计算阶乘会炸精度,记 f a c [ i ] = l o g ( i ! ) fac[i]=log(i!) fac[i]=log(i!),那么 f a c [ i ] = l o g ( ( i − 1 ) ! ∗ i ) = f a c [ i − 1 ] + l o g ( i ) fac[i]=log((i-1)!*i)=fac[i-1]+log(i) fac[i]=log((i−1)!∗i)=fac[i−1]+log(i)。 C ( n , m ) = e x p ( f a c [ n ] − f a c [ m ] − f a c [ n − m ] ) C(n,m)=exp(fac[n]-fac[m]-fac[n-m]) C(n,m)=exp(fac[n]−fac[m]−fac[n−m])。组合数的乘除都可以转化为指数的加减,较好地保证了精度。
Code(CF似乎是windows环境评测,所以输出用不了Lf,转为double输出):
#include<bits/stdc++.h>
#define LD long double
using namespace std;
int n,m,k;
LD fac[100005],ans;
inline LD C(int n,int m){return fac[n]-fac[m]-fac[n-m];}
int main()
{
scanf("%d%d%d",&n,&m,&k);
for(int i=2;i<=m;i++) fac[i]=fac[i-1]+log(i);
for(int r=0;r<=n;r++)
for(int c=0,x;c<=n;c++) if((x=(r+c)*n-r*c)<=k)
ans+=exp(C(n,r)+C(n,c)+C(m-x,k-x)-C(m,k));
if(ans<1e99) printf("%.10f\n",(double)ans);
else puts("1e99");
}