1. 程式人生 > >【JZOJ 3871】 無聊的遊戲

【JZOJ 3871】 無聊的遊戲

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)才能覆蓋出這些行列,這些點的數必選,其餘可以選擇其他任意點
所以Cktmt/Ckm為成功概率p
那麼答案即為

i=0nj=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; }