题目

\(n\) 堆石子,每堆石子的数量是 \(i\) 的概率是 \(p_i\)。两人轮流取石子,每次可以选任一堆取走任意数量的石子,但不能不取,不能取的人输,问在双方采取最佳策略的前提下先手必胜的概率。

\(n\le 10^9\),石子数量最大值满足在 \([1,100]\).

解法

首先我们知道 Nim 游戏的一个结论:当石子数异或和为 0 时先手必败。

问题就转化成求异或和为零的概率,再用 1 减去这个概率就行了。

我们令 \(dp_{i,j}\) 为前 \(i\) 堆石子异或和为 \(j\) 的概率。那么就有:

\[dp[i][j⊕k]=\sum_{k=0}^{127}dp[i-1][k]\cdot p[j] \]

我们用矩阵加速来加速。\(10^9\) 大约是 \({2^{27}}\) 级别,又因为 \(x\) 的最大值为 \(100\),二进制有七位,而七位二进制最大的是 \(127\)。所以时间复杂度为 \({\mathcal O(128^3\times 27)}\) 可过(不过实际也没有这么大)。

代码

#include <cstdio>
#include <cstring>

const int lim = 128;

int n, x;
double t;
struct Matrix {
    double a[lim][lim]; int n, m;

    Matrix() {memset(a, 0, sizeof a);}

    Matrix operator * (const Matrix &b) {
        Matrix r; r.n = n; r.m = b.m;
        for(int i = 0; i <= r.n; ++ i)
            for(int j = 0; j <= r.m; ++ j)
                if(a[i][j])
                    for(int k = 0; k <= m; ++ k)
                        r.a[i][k] += a[i][j] * b.a[j][k];
        return r;
    }
}per, ans;

Matrix qkpow(int y) {
    Matrix ret; ret.n = ret.m = lim - 1;
    for(int i = 0; i <= ret.n; ++ i) ret.a[i][i] = 1;
    while(y) {
        if(y & 1) ret = ret * per;
        per = per * per; y >>= 1;
    }
    return ret;
}

int read() {
    int x = 0, f = 1; char s;
    while((s = getchar()) < '0' || s > '9') if(s == '-') f = -1;
    while(s >= '0' && s <= '9') {x = (x << 1) + (x << 3) + (s ^ 48); s = getchar();}
    return x * f;
}

void init() {
    per.n = per.m = lim - 1;
    for(int i = 0; i <= x; ++ i) {
        scanf("%lf", &t);
        for(int j = 0; j < lim; ++ j)
            per.a[j][i ^ j] = t;
    }
}

int main() {
    n = read(), x = read();
    init();
    ans = qkpow(n);
    printf("%.10f\n", 1.0 - ans.a[0][0]);
    return 0;
}
posted on 2020-04-08 16:27  Oxide  阅读(178)  评论(0编辑  收藏  举报