CodeForces

题目

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

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

解法

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

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

我们令 (mathtt{dp[i][j]}) 为前 (i) 堆石子异或和为 (j) 的概率。

那么就有:

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

我们用矩阵加速来加速。(mathtt{1e9}) 大约是 (mathtt{2^{27}}) 级别,又因为 (x) 的最大值为 (100),二进制有七位,而七位二进制最大的是 (127)。所以时间复杂度为 (mathtt{O(128^3*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
", 1.0 - ans.a[0][0]);
    return 0;
}
原文地址:https://www.cnblogs.com/AWhiteWall/p/12660769.html