期望+高斯消元
因为是个环,不妨把这 \(n\) 只鹿想成排了一队,每次是队首拿加特林,他开一枪只有两种可能,要么在那 \(P_0\) 的概率里中枪了,要么在剩下的 \(1-P_0\) 的概率里没中枪,没中枪的话他就跑到队尾去当最后一只鹿了。
设 \(f_{i,j}\) 表示有 \(i\) 只鹿,第 \(j\) 只赢的概率。
就有了一个比较好想的状态转移方程:
其中 \(f_{i-1,j-1}\) 表示当前这只鹿死了,整体来看少了一只,这是有 \(P_0\) 的概率的;\(f_{i-1,j-1}\) 表示当前这只鹿没死,总数不变,这是有 \((1-P_0)\) 的概率的。
因为最后有且仅有一个幸存者,所以所有鹿做幸存者的概率和为 \(1\),得到:
\(i\) 个方程找全了,看起来可以高斯消元了 ,但是 \(n\) 的范围是 \(10^4\),现在需要考虑优化,也就是手动高斯消元。
尝试把上面那个转移看成一个 \(y=kx+b\) 形式的一次函数,设 \(y=f_{i,j}\),\(x=f_{i,j-1}\),那么 \(k=(i-P_0)\),常数项就是 \(P_0f_{i-1,j-1}\)。
发现这样设是可行的,因为 \(f_{i-1,j-1}\) 是在之前已经算出来的,而 \(f_{i,j-1}\) 可以由 \(f_{i,j-2}\) 得到,\(f_{i,j-2}\) 可以由 \(f_{i,j-3}\) 得到,\(f_{i,j-3}\) 可以由 \(f_{i,j-4}\) 得到……直到这一串由 \(f_{i,1}\) 得到。
所以 \(f_{i,j}\) 就是一个关于 \(f_{i,1}\) 的一次函数。求出这个函数,代入最后一个方程 \(a\cdot f_{i,1}+b=1\),就能求出 \(f_{i,1}\), 进而求出所有的。
注意特判一下 \(P_0=0\) 的情况。
\(code\)
#include<bits/stdc++.h>
#define N 1000010
#define int long long
#define debug cout<<"------------------"<<endl
using namespace std;
int n,k;
int now=1,lst=0;
double p0;
double fac[N],f[2][N];
signed main(){
cin>>p0>>n>>k;
if(p0==0) printf("%d\n",(n==1)?1:0),exit(0);
fac[0]=1;
for(int i=1;i<=n;i++) fac[i]=(1-p0)*fac[i-1];
f[1][1]=1;
for(int i=2;i<=n;i++){
lst=now,now^=1;
double sum=0;
for(int j=1;j<=i-1;j++) sum+=fac[i-j]*f[lst][j];
for(int j=1;j<=i;j++){
f[now][j]=sum;
sum=(1-p0)*(sum-fac[i-1]*f[lst][j])+f[lst][j];
}
sum=p0/(1-fac[i]);
for(int j=1;j<=i;j++) f[now][j]*=sum;
}
printf("%.10f",f[now][k]);
return 0;
}