题意:一条线上有N个雷子,一个人需要经过,p概率往前走一步,1-p概率往前跳2步,从1开始走,问这个人经过的概率,就是不会被任何一个雷子炸到的概率
题解:
设A=p
B=1-p
从1开始,走到2的概率为 A
走到3的概率为 B+A*A
走到4的概率为AB+A*A*A+A*B
……
因为
然后区间太长,需要矩阵快速幂
矩阵:
A B
1 0
然后求n次方就是n次跳跃后点的概率
这里求得踩雷的概率temp,用1-temp就是通过的概率,挺难理解的。
这样每次的通过概率求积事件的概率
AC代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N =2;
const int maxm = 15;
int num[maxm];
int n = 2;
int m;
double p;
//矩阵快速幂
struct Mat{
double mat[N][N];
};
Mat operator *(Mat a, Mat b){
Mat c;
memset(c.mat, 0, sizeof(c.mat));
for(int k = 0; k < n; ++k){
for(int i = 0; i < n; ++i){
if(a.mat[i][k] <= 0) continue;//(针对ZOJ2853)剪枝,cpu运算乘法的效率并不是想像的那么理想(加法的运算效率高于乘法,比如Strassen矩阵乘法)
for(int j = 0; j < n; ++j){
if(b.mat[k][j] <= 0) continue; //剪枝
c.mat[i][j] += a.mat[i][k] * b.mat[k][j];
}
}
}
return c;
}
Mat operator ^ (Mat a, int k) {
Mat c;
int i, j;
memset(c.mat, 0, sizeof(c.mat));
for(int i = 0; i<n; ++i) c.mat[i][i] = 1;
for(; k; k >>= 1) {
if(k&1) c = c*a;
a = a*a;
}
return c;
}
double solve(){
sort(num+1, num+m+1);
num[0] = 0;
for(int i =1; i<=m; ++i){
if(num[i-1]+1==num[i]) return 0.0;
}
double temp;
double ans = 1.0;
for(int i =1; i<=m; ++i){
int len = num[i]-num[i-1]-1;
if(num[i] == num[i-1]) continue;
Mat t;
t.mat[0][0]=p;
t.mat[0][1]=1-p;
t.mat[1][0]=1;
t.mat[1][1]=0;
t=t^len;
temp = 1-t.mat[0][0];
ans*=temp;
}
return ans;
}
int main(){
while(scanf("%d%lf", &m, &p)!=EOF){
for(int i = 1; i <= m; ++i){
scanf("%d", num + i);
}
printf("%.7f\n", solve());
}
return 0;
}