我們先把鑽石的位置依照對原點的距離(|X|+|Y|)分成好多層,像屋頂一樣,定原點那顆鑽石的位置為第1層
可以觀察到,鑽石要跑到第n層之前,必須先把第n-1層填滿(除非n=1)
因此,我們可以先讓鑽石下落,直到剩餘的鑽石無法再填滿新的一層為止,這時鑽石會堆成一個完美的45度等腰三角形(以下叫做金字塔),機率為1
如果(X,Y)在金字塔裡面,那麼答案是1.0
如果新的一層填滿了,還是碰不到(X,Y),那麼答案是0.0
再來可以發現,X的正負號不影響答案,如果X=0,那麼答案為0.0(想一想,為甚麼XD)
所以我們只考慮X>0,也就是右側的情況
我們可以先算出沒有鑽石的機率,再用1.0去扣掉
設右側至少要堆k顆鑽石才能到達(X,Y),剩下r顆鑽石可以堆
考慮每一顆鑽石掉下來會有各一半的機率往左或往右走,因此我們得到沒有鑽石的機率是
Sum{C(r,i)/(2^r),i=0~k-1}
注意,堆在左側的鑽石數量為r-i,而這個數字有可能大於金字塔的高度!
這時,有幾顆鑽石會因為疊太高而滑到右邊來,只要確認一下這種情況右側的鑽石有沒有達到k顆即可
算完以上機率後,用1.0減掉就是答案了
或者像我一樣直接從1.0慢慢扣也是可以
然後別像我一樣也把double寫成int然後一直回傳0和1 XD
code:
#include<cstdio> #include<cmath> #include<cassert> #include<vector> using namespace std; int N,X,Y; const double LogTwo=log(2.0); vector<double>LEVEL; double Level(const int i) { if(i<(int)LEVEL.size())return LEVEL[i]; LEVEL.push_back(Level(i-1)+log(i)); // printf("level %d=%.9f\n",i,exp(LEVEL[i])); return LEVEL[i]; } double F(const int a,const int b) { const double ans=exp(Level(a)-Level(b)-Level(a-b)-a*LogTwo); // printf("F(%d,%d)=%.9f\n",a,b,ans); return ans; } double Solve() { //1,6,15,28 int v=sqrt(N*2); if(v*(v+1)/2>N)v--; assert(v*(v+1)/2<=N&&(v+1)*(v+2)/2>N); if(v%2==0)v--; const int remain=N-v*(v+1)/2; // printf("v=%d\n",v); if(v-1>=X+Y)return 1.0; else if(v+1<X+Y)return 0.0; // puts("pass"); if(Y==v+1)return 0.0; double ans=1.0; for(int i=0;i<=Y&&i<=remain;i++)if(remain-i<=v+1||i+(remain-i)-(v+1)<=Y)ans-=F(remain,i); return ans; } int main() { LEVEL.push_back(0.0); int testcase;scanf("%d",&testcase); while(testcase--) { scanf("%d%d%d",&N,&X,&Y); if(X<0)X=-X; if((X+Y)%2==1) { if(Y==0)X--; else Y--; } printf("%.9f\n",Solve()); } return 0; } /* 5 1 2 */
沒有留言:
張貼留言
歡迎留言或問問題~
若您的留言中包含程式碼,請參考這篇
如果留言不見了請別慌,那是因為被google誤判成垃圾留言,小莫會盡快將其手動還原
注意:只有此網誌的成員可以留言。