[Sdoi 2017] bzoj4820 硬币游戏 [概率+高斯消元+哈希]

12 篇文章 0 订阅
2 篇文章 0 订阅

Description:
给出 n n 个长度均为m的不同 01 01 串,随机生成一个无限长的 01 01 串,对 n n 01串中的每个,求出它最先在随机串中出现的概率.


Solution:
N N <script type="math/tex" id="MathJax-Element-432">N</script>为不为任意一个串的概率,那么考虑N后添加一个串A,但是可能中途生成B。那么我们计算这个概率,也就是A的前缀和B的后缀相等,相差的字符的概率,求和之后高斯消元即可。


#include <bits/stdc++.h>
using namespace std;
const int maxn = 305;
int n, m;
long long h[maxn][maxn], bb[maxn];
char s[maxn][maxn], S[maxn];
double a[maxn][maxn], pw[maxn];
void gauss(int n) {
    for(int i = 1; i <= n; ++i) {
        int p = i;
        for(int j = i + 1; j <= n; ++j) {
            if(fabs(a[j][i]) > fabs(a[p][i])) {
                p = j;
            }
        }
        for(int j = 1; j <= n + 1; ++j) {
            swap(a[i][j], a[p][j]);
        }
        double t = a[i][i];
        for(int j = 1; j <= n + 1; ++j) {
            a[i][j] /= t;
        }
        for(int j = 1; j <= n; ++j) {
            if(j != i) {
                double t = a[j][i];
                for(int k = 1; k <= n + 1; ++k) {
                    a[j][k] -= a[i][k] * t;
                }
            }
        }
    }
}
int main() {
    scanf("%d%d", &n, &m);
    for(int i = 1; i <= n; ++i) {
        scanf("%s", s[i] + 1);
        for(int j = 1; j <= m; ++j) {
            h[i][j] = h[i][j - 1] * 19992147 + s[i][j]; 
        }
    }
    pw[0] = 1;
    bb[0] = 1;
    for(int i = 1; i <= m; ++i) {
        pw[i] = pw[i - 1] * 0.5;
        bb[i] = bb[i - 1] * 19992147;
    }
    for(int i = 1; i <= n; ++i) {
        for(int j = 1; j <= n; ++j) {
            for(int k = 1; k <= m; ++k) {
                if(h[i][k] == h[j][m] - h[j][m - k] * bb[k]) {
                    a[i][j] += pw[m - k];
                }
            }
        }
        a[i][n + 1] = -pw[m];
    }
    for(int i = 1; i <= n; ++i) {
        a[n + 1][i] = 1;
    }
    a[n + 1][n + 2] = 1;
    gauss(n + 1);
    for(int i = 1; i <= n; ++i) {
        printf("%.10f\n", a[i][n + 2]);
    }
    return 0;
} 
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值