Description
学校的运动会开始了,体能很菜的小可可没报任何比赛项目,于是和同学们玩一个十分无聊的游戏。
游戏在一个由n*n个方格组成的正方形棋盘上进行,首先在每个方格上均匀随机地填入1到m之间的正整数(每个方格填的数均不同),然后小可可均匀随机地选出k个1到m的数字(可能选的数不在棋盘上),把它们出现在棋盘上的方格涂黑,设有R行被整行涂黑,有C列被整列涂黑,小可可便可以得到2^(R+C)分。
现在小可可想知道他的期望得分是多少,你能帮助他吗?
Solution
2^(R+C),注意一下就可以发现,这是一个2n位的二进制数的全部子集,
还有,题目所谓的填数,其实并没有什么用,
很显然最后的答案就等于所有的权值和/总方案数,
而总方案数=
Ckm
,
设
gR,C
,R,C是两个集合,表示它的子集的个数,
设
fR,C
,R,C是两个集合,表示有多少种选数的方案可以达到有R,C这两个集合的状态,
Ans=∑fR,C∗gR,CCkm
把最后除的数先不管,
显然,f和g的值只与R和C中为1的个数有关,
转化一下,只要计算
gr,c
和
fr,c
(有r列、c行是满的)即可
再转化一下,计算每一个r,c在答案中被计算了多少次,发现f,g其实可以合并:
Ans=∑r=0n∑c=0nCrnCcn∗Ck−tm−t
t表示一定要选t个才能达到r,c这个状态,最后一项恰好包含了f,g这两个,
PS:本题可以用C++的double跑过,不要问我为什么。
Code
#include <cstdio>
#include <cstdlib>
#define fo(i,a,b) for(int i=a;i<=b;i++)
using namespace std;
typedef long double db;
const int N=1e5+50;
int m,n,K;
db C[350],ans,d[N];
int main()
{
scanf("%d%d%d",&n,&m,&K);
C[0]=1;fo(i,1,n)C[i]=C[i-1]*(db)(n-i+1)/(db)i;
d[0]=1;d[1]=1.0*K/m;fo(i,2,K)d[i]=d[i-1]*(db)(K-i+1)/(db)(m-i+1);
fo(i,0,n)fo(j,0,n)ans+=C[i]*C[j]*d[(i+j)*n-i*j];
if(ans>1e99)ans=1e99;
printf("%.9lf\n",(double)ans);
return 0;
}