CF457D Bingo!
题目描述:
有一个\(n×n(n≤300)\)的棋盘和\(1~m(n^2≤m≤100000)\) 这些数字。棋盘首先会被随机生成,即从“填着值域\(1 \sim m\)的数字且\(n^2\)个数字两两不同”的所有方案中随机选一个。
然后你会从\(1\sim m\)中随机选出\(k(n≤k≤m)\)个不同的数,并且报出来。报出一个数字,如果在棋盘中出现过,就把对应格涂黑。设\(t\)为完全染黑的行数、完全染黑的列数的和,你的最终得分为\(2^t\)求得分的期望。如果超过\(10^{99}\),输出\(1e99\)
思路:
观察到题目中有一个非常恼人的 \(2^r\)。我们考虑一下这个东西的实际含义:从 \(t\) 个染黑的行和列中选一个子集(一些行+一些列)的方案数
所以我们不妨去计算对于一个有 \(r\) 行,\(c\) 列的集合是多少集合的子集,这个就是这个集合对答案的贡献。
首先我们思考一下总方案数是多少:\(\binom{m}{n^2}\times \binom{m}{k}\)
然后我们枚举选了 \(r\) 行 和 \(c\) 列,总贡献是多少:\(\sum\limits_{r=0}^{n}\sum\limits_{c=0}^{n}\binom{m}{n^2}\binom{n}{r}\binom{n}{c}\binom{m-num}{k-num},num=(r+c)\times n-r-c\)
简单解释一下这个式子是什么意思:首先你需要选一个棋盘,方案数 \(\binom{m}{n^2}\);然后你要选一些行和一些列作为被涂黑的行和列: \(\binom{n}{r}\binom{n}{c}\);然后我们这 \(r\) 行 \(c\) 列覆盖了一共 \((r+c)\times n-r-c\) 个格子,这些格子都必须被涂黑,所以除去这些格子之后还有 \(\binom{m-num}{k-num}\) 种方案数。
所以总方案数为 \(Ans=\frac{\sum\limits_{r=0}^{n}\sum\limits_{c=0}^{n}\binom{m}{n^2}\binom{n}{r}\binom{n}{c}\binom{m-num}{k-num}}{\binom{m}{n^2}\binom{m}{k}}=\frac{\sum\limits_{r=0}^{n}\sum\limits_{c=0}^{n}\binom{n}{r}\binom{n}{c}\binom{m-num}{k-num}}{\binom{m}{k}}\)
点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define double long double
const int maxn=1e5+5;
int n,m,k;
double f[maxn];
double C(int n,int m){
return f[n]-f[m]-f[n-m];
}
void init(){
int N=maxn-5;
for(int i=1;i<=N;i++)f[i]=f[i-1]+log(1.0*i);
}
double ans;
signed main(){
cin>>n>>m>>k;
init();
for(int i=0;i<=n;i++){
for(int j=0;j<=n;j++){
int num=(i+j)*n-i*j;
if(num<=k){
double p=C(n,i)+C(n,j)+C(m-num,k-num)-C(m,k);
ans=min(ans+exp(p),(double)(1e99));
}
}
}
printf("%.10Lf\n",ans);
return 0;
}