【NOIP2014八校联考第4场第1试10.19】无聊的游戏(game)

Description

学校的运动会开始了,体能很菜的小可可没报任何比赛项目,于是和同学们玩一个十分无聊的游戏。
游戏在一个由n*n个方格组成的正方形棋盘上进行,首先在每个方格上均匀随机地填入1到m之间的正整数(每个方格填的数均不同),然后小可可均匀随机地选出k个1到m的数字(可能选的数不在棋盘上),把它们出现在棋盘上的方格涂黑,设有R行被整行涂黑,有C列被整列涂黑,小可可便可以得到2^(R+C)分。
现在小可可想知道他的期望得分是多少,你能帮助他吗?

Solution

这道不是概率、期望的DP题,而是一道数论题。
因为涂的方案和选的颜色是没有关联的,所以我们直接去算选的颜色就好了。
首先, 2R+C 表示涂黑了R行C列之后,它的所有子集。
那么我们枚举所有的涂色集合包含了多少个子集和一个子集被多少个集合包含是一样的。
所以 ans=CrnCcnq(r,c)
q(r,c)表示涂色包含r行c列的概率: CktmtCkm ,t=n*(r+c)*rc(表示r行c列的颜色占有的格子数,即要用多少颜色)。
注意,double不能直接预处理阶乘,要预递推处理 Cinq(r,c)

Code

#include
#include
#include
#include
#include
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
const int maxn=307;
int i,j,k,l,t,n,m,r,cc;
double ans,ans1,fact[maxn*maxn],o,a[maxn*maxn],b[maxn*maxn];
double c(int x,int y){
    return fact[x]/fact[y]/fact[x-y];
}
double q(int x,int y){
    int t=n*(x+y)-x*y;
    double o=0;
    if(k-t<0)return 0;
  //  o=c(m-t,k-t)/c(m,k);
    o=b[t];
    return o;
}
int main(){
    //freopen("fan.in","r",stdin);
    scanf("%d%d%d",&n,&m,&k);
    fact[0]=1;a[0]=b[0]=1;
    fo(i,1,m)a[i]=a[i-1]*(n-i+1)/i,b[i]=b[i-1]*(k-i+1)/(m-i+1);
    fo(r,0,n){
        fo(cc,0,n){
            ans+=a[r]*a[cc]*q(r,cc);
        }
    }
    if(ans>1e99){
        ans=1e99;
    }
    printf("%.6lf\n",ans);
}

你可能感兴趣的:(noip,概率,数论)