Description
学校的运动会开始了,体能很菜的小可可没报任何比赛项目,于是和同学们玩一个十分无聊的游戏。
游戏在一个由n*n个方格组成的正方形棋盘上进行,首先在每个方格上均匀随机地填入1到m之间的正整数(每个方格填的数均不同),然后小可可均匀随机地选出k个1到m的数字(选的数不可重复,可能选的数不在棋盘上),把它们出现在棋盘上的方格涂黑,设有R行被整行涂黑,有C列被整列涂黑,小可可便可以得到2^(R+C)分。
现在小可可想知道他的期望得分是多少,你能帮助他吗?
对于100%的数据,2≤n≤300, n*n≤m≤100000, n≤k≤m。
Analysis
很好的组合数学题,这题我学到了一些思想
每一行列的涂黑与否可以变成二进制
首先这个答案为2^(R+C),等价于一个状态的子集的个数(只能说这一步转化打死我也想不到)
朴素思路即枚举行列状态,计算其子集个数,但这很难算
正难则反
枚举行列状态,计算其作为多少状态的子集
这好算,因为最少要用n(r+c)-rc个点(设为t)才能覆盖出这些行列,这些点的数必选,其余可以选择其他任意点
所以
Ck−tm−t/Ckm
为成功概率p
那么答案即为
∑i=0n∑j=0nCinCjnp
枚举行列有多少个被涂黑,那么前两个组合数就是所有可能行列个数。
这东西理解容易,但是自己想到却很难
Code
#include<cstdio>
#include<algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
using namespace std;
const int N=100005;
double n,m,k,ans,c[N],b[N];
int main()
{
scanf("%lf %lf %lf",&n,&m,&k);
c[0]=1;
fo(i,1,n) c[i]=c[i-1]/i*(n-i+1);
b[0]=1;
fo(j,1,n*n) b[j]=b[j-1]*(k-j+1)/(m-j+1);
fo(i,0,n)
fo(j,0,n)
{
int t=n*(i+j)-i*j;
ans=ans+c[i]*c[j]*b[t];
}
if(ans>1e99) printf("%lf",1e99);
else printf("%lf",ans);
return 0;
}