题解
题意:两个女孩玩Nim游戏,n个堆,每个堆的石子数均在0~x之间,石子数为i的概率是p[i],问先手能赢的概率
Nim游戏的套路:
对于一个Nim游戏的局面(a1,a2,…,an),当且仅当a1 ^ a2 ^ … ^ an=0,先手必败
所以只要找出满足最后异或值为0的所有可能的概率ans,答案就是1-ans,
不过n太大了,1e6,不可能枚举,
所以从x入手,0~100的所有异或值最大不会超过127,
然后用矩阵快速幂转移,转移矩阵由p[i]组成,E.mat[i][i ^ j] = p[j];// E.mat[异或前的值][异或后的值]
,
矩阵快速幂乘个n次就是转移n堆,最后必败的概率就是在E.mat[0][0]的位置上
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e6 + 10;
int n, x, K;
double p[N];
namespace matrix {//矩阵快速幂板子
const int maxn = 128;
struct Mat {
double mat[maxn][maxn];
void clear() {
memset(mat, 0, sizeof(mat));
}
Mat operator*(const Mat a) const {
Mat b;
b.clear();
for (int i = 0; i < maxn; ++i) {
for (int j = 0; j < maxn; ++j) {
for (int k = 0; k < maxn; ++k) {
b.mat[i][j] = b.mat[i][j] + mat[i][k] * a.mat[k][j];
}
}
}
return b;
}
};
Mat pow(Mat m, ll k) {
Mat res;
res.clear();
for (int i = 0; i < maxn; ++i) {//单位矩阵
res.mat[i][i] = 1;
}
while (k) {
if (k & 1)res = res * m;
k >>= 1;
m = m * m;
}
return res;
}
Mat E;//转移矩阵
}
using namespace matrix;
int main() {
ios::sync_with_stdio(0);
cin >> n >> x;
for (int i = 0; i <= x; ++i) {
cin >> p[i];
}
E.clear();
for (int i = 0; i <= 127; ++i) {
for (int j = 0; j <= x; ++j) {
E.mat[i][i ^ j] = p[j];// E.mat[异或前的值][异或后的值]
}
}
Mat ans = pow(E, n);//中间转移n次
printf("%.8f\n", 1 - ans.mat[0][0]);
return 0;
}