概率dp刷题
- 题意:有一个 \(n\) 堆石子的 \(nim\) 游戏,石子数量从 \(0\) 到 \(x\) 有一个概率 \(p_i\) ,代表了一堆石子数量为 \(i\) 的数量。求先手必赢的概率。
- 题解:首先nim显然的是 \(nim\) 异或和为 \(0\) 是必输,然后可以构造 \(A\) 矩阵
\(A =\begin{bmatrix} p[0 \oplus 0 ] &p[0 \oplus 1] & p[0 \oplus 2]\\ p[1 \oplus 0 ] &p[1 \oplus 1] & p[1 \oplus 2]\\ p[2 \oplus 0 ] &p[2 \oplus 1] & p[2 \oplus 2] \end{bmatrix}\)
\(ans\) 矩阵就是 \(ans = \begin{bmatrix}
dp[0] &0 & 0\\
dp[1] &0 & 0\\
dp[2] &0 & 0
\end{bmatrix}\)
\(ans_{1, j}\) 代表的意思是,在这一次,\(dp[j]\) ,即游戏出现他们异或和为\(j\) 情况的概率。
- 代码:
#include<bits/stdc++.h>
#pragma comment(linker, "/STACK:102400000,102400000")
using namespace std;
typedef long long ll;
typedef long double ld;
const ll N = 129;
const ll inf = 0x3f3f3f3f;
ld pp[N];
const ll maxn = 10;
struct Matrix {
ld a[maxn][maxn];
int n;
Matrix (int sz, int kind) {
this->n = sz;
for (int i = 0; i < sz; i ++) {
for (int j = 0; j < sz; j ++) {
a[i][j] = 0;
}
}
for (int i = 0; i < sz; i ++) {
a[i][i] = kind;
}
}
Matrix operator*(Matrix rhs)const {
Matrix ret(N - 1, 0);
for (int i = 0; i < n; i ++) {
for (int j = 0; j < n; j ++) {
for (int k = 0; k < n; k ++) {
ret.a[i][j] += a[i][k] * rhs.a[k][j];
}
}
}
return ret;
}
void out() {
for (int i = 0; i < 5 ;i ++) {
for (int j = 0; j < 5; j ++) {
cout << a[i][j] << " ";
}
cout << endl;
}
}
} A(maxn - 1, 0);
Matrix q_pow( int k) {
Matrix ret = {N - 1, 1};
Matrix x = A;
while (k) {
if (k & 1) {
ret = ret * x;
}
k >>= 1;
x = x * x;
}
return ret;
}
void solve() {
int n, x;
cin >> n >> x;
for (int i = 0; i <= x; i ++) {
cin >> pp[i];
}
cout << fixed<<setprecision(10);
for (int i = 0; i <= N-2; i ++) {
for (int j = 0; j <= N-2; j ++) {
A.a[i][j] = pp[j ^ i];
}
}
Matrix res = q_pow( n);
Matrix d = Matrix(N - 1, 0);
d.a[0][0] = 1;
res = d * res;
cout << 1.0-res.a[0][0] << endl;
}
signed main() {
ll t = 1;//cin >> t;
while (t--) {
solve();
}
}