SP4060 KPGAME - A game with probability

题目链接:SP4060

Alice和Bob在玩一个游戏。

有n个石子在这里,Alice和Bob轮流投掷硬币,如果正面朝上,则从n个石子中取出一个石子,否则不做任何事。取到最后一颗石子的人胜利。Alice在投掷硬币时有p的概率投掷出他想投的一面,Bob有q的概率投掷出他相投的一面。

现在Alice先手投掷硬币,假设他们都想赢得游戏,问你Alice胜利的概率为多少。

$T\leq 50, n\leq 10^8, 0.5\leq p,q < 1$。

Tutorial

不妨设 $dp[i][0/1]$ 表示目前是 Alice/Bob 投掷硬币,还剩 $i$ 个石子,Alice 胜利的概率。

显然有 $dp[0][0]=1, dp[0][1]=0$。转移的话需要分两类情况讨论:

若 $dp[i-1][0] > dp[i-1][1]$,说明在剩余 $i-1$ 个石子时,若轮到 Alice 投掷硬币,Alice 获胜的概率更大,因此 Alice 因想尽办法尽可能在剩余第 $i$ 个石子时,投掷反面,让 Bob 拿走第 $i$ 颗石子。Bob 为了让 Alice 尽可能输,因此他希望在剩余 $i$ 个石子时,他能够投掷反面,让 Alice 拿走第 $i$ 颗石子。

同理,若 $dp[i-1][0] < dp[i-1][1]$,Alice 和 Bob 都想拿走第 $i$ 颗石子。

因此,我们可以列出转移方程。

$$
\begin{aligned}
dp[i][0] = dp[i-1][1] \times p + dp[i][1] \times (1-p)\\
dp[i][1] = dp[i-1][0] \times q + dp[i][0] \times (1-q)
\end{aligned}
$$

其中,$p,q$ 在 $dp[i-1][0] < dp[i-1][1]$ 时,值为 $A,B$,否则为 $1-A,1-B$。

注意到当 $i$ 增大的同时,$dp$ 数组的值会不断减小,因此之后可以忽略不记,所以可以将 $n$ 与 $1000$ 取 $\min$ 即可通过本题。

当然,如果你感觉比较好,会发现 $p,q$ 的取值与 $i$ 的奇偶性有关,因此可以两步一起跑,矩阵快速幂加速转移即可。

复杂度 $O(TN\log N)$。

Solution

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define W while
#define I inline
#define RI register int
#define LL long long
#define Cn const
#define CI Cn int&
#define gc getchar
#define D isdigit(c=gc())
#define pc(c) putchar((c))
#define min(x,y) ((x)<(y)?(x):(y))
#define max(x,y) ((x)>(y)?(x):(y))
using namespace std;
namespace Debug{
Tp I void _debug(Cn char* f,Ty t){cerr<<f<<'='<<t<<endl;}
Ts I void _debug(Cn char* f,Ty x,Ar... y){W(*f!=',') cerr<<*f++;cerr<<'='<<x<<",";_debug(f+1,y...);}
Tp ostream& operator<<(ostream& os,Cn vector<Ty>& V){os<<"[";for(Cn auto& vv:V) os<<vv<<",";os<<"]";return os;}
#define gdb(...) _debug(#__VA_ARGS__,__VA_ARGS__)
}using namespace Debug;
namespace FastIO{
Tp I void read(Ty& x){char c;int f=1;x=0;W(!D) f=c^'-'?1:-1;W(x=(x<<3)+(x<<1)+(c&15),D);x*=f;}
Ts I void read(Ty& x,Ar&... y){read(x),read(y...);}
Tp I void write(Ty x){x<0&&(pc('-'),x=-x,0),x<10?(pc(x+'0'),0):(write(x/10),pc(x%10+'0'),0);}
Tp I void writeln(Cn Ty& x){write(x),pc('\n');}
}using namespace FastIO;
Cn int N=1e6+10;
int Tt,n;double A,B,f[N][2],p,q;
int main(){
RI i;read(Tt);W(Tt--){
for(read(n),scanf("%lf%lf",&A,&B),assert(0.5<=A&&A<=1&&0.5<=B&&B<=1),f[0][0]=0,f[0][1]=1,i=1;i<=min(n,1000);i++)
f[i-1][1]>f[i-1][0]?(p=A,q=B):(p=1-A,q=1-B),f[i][0]=(f[i-1][0]*q*(1-p)+f[i-1][1]*p)/(1-(1-q)*(1-p)),f[i][1]=f[i][0]*(1-q)+f[i-1][0]*q;
printf("%.6lf\n",f[min(n,1000)][0]);
}return 0;
}