HDU 5955 Guessing the Dice Roll

题目大意:
有一个6面的骰子,有n (n10) 个人每个人猜了一个长度为l (l10) 的序列,不停的掷骰子直到满足一个人的序列则那个人获胜,求每个人获胜的概率。
题解:
由题可知,状态总数最多为100。根据每个人的序列建立AC自动机,根据自动机状态转移构建矩阵 M M[i][j]表示从j转移到i的概率,转移矩阵包含终态到自己的转移。因为这个矩阵是收敛的,因此可以快速幂卡过。注意指数取 2i 形式以减少常数。

另一种方法是高斯消元,设增量矩阵为 A ,初始向量为b,最终答案为 x ,则有
x=iAib
该题中增量矩阵 A 的幂级数是收敛的,且等于(IA)1,因此上式变成 (IA)x=b ,高斯消元即可。

#include <bits/stdc++.h>

using namespace std;

const int MN = 105;
const int Z = 6;
const int maxlen = 10005;
int gue[11];

typedef long long ll;
class matrix{
public:
    int row;
    int col;
    double m[105][105];

    matrix(int r,int c):row(r),col(c){
        memset(m,0,sizeof(m));
    }

    matrix(int n){
        memset(m,0,sizeof(m));
        row = col = n;
        for(int i = 0;i < n;i++){
            m[i][i] = 1;
        }
    }

    matrix(){}

    matrix operator*(const matrix &b){
        matrix ret(row,b.col);
        for(int i = 0;i < row;i++){
            for(int k = 0;k < col;k++){
                for(int j = 0;j < b.col;j++){
                    ret.m[i][j] += m[i][k]*b.m[k][j];
                }
            }
        }
        return move(ret);
    }

    matrix qpow(ll n){
        matrix ret(row);
        matrix tmp = *this;
        while(n){
            if(n&1){
                ret = move(ret * tmp);
            }
            tmp = move(tmp * tmp);
            n>>=1;
            //tmp.print();
            //printf("\n");
        }
        return move(ret);
    }

    void print(){
        for(int i= 0;i < row;i++){
            for(int j =0;j < col;j++){
                printf("%f ",m[i][j]);
            }
            printf("\n");
        }
    }
}A;

class ACautomaton {
public:
    int chd[MN][Z], fail[MN], Q[MN], nn;
    int fin[11];
    bool Mark[MN];

    void Init() {
        fail[0] = 0;
        memset(chd[0], 0, sizeof(chd[0]));
        memset(Mark, false, sizeof(Mark));
        nn = 1;
    }

    void Insert(int id,int l) {
        int p = 0;
        for(int i = 0;i < l;i++) {
            int c = gue[i]-1;
            if(!chd[p][c]) {
                memset(chd[nn], 0, sizeof(chd[nn]));
                chd[p][c] = nn++;
            }
            p = chd[p][c];
        }
        fin[id] = p;
        Mark[p] = true;
    }

    void Build() {
        int *s = Q, *e = Q;
        for(int i = 0; i < Z; i++) {
            if(chd[0][i]) {
                *e++ = chd[0][i];
                fail[chd[0][i]] = 0;
            }
        }
        while(s != e) {
            int u = *s++;
            for(int i = 0; i < Z; i++) {
                int v = chd[u][i];
                if(v){
                    *e++ = v;
                    int t = fail[u];
                    while(t && !chd[t][i]) t = fail[t];
                    fail[v] = chd[t][i];
                }
                //else{
                //    int t = fail[u];
                //    while(t&&!chd[t][i]) t = fail[t];
                //    chd[u][i] = chd[t][i];
                //}
            }
        }
        A = matrix(nn,nn);
        for(int i = 0;i < nn;i++){
            if(Mark[i]){
                A.m[i][i] = 1.0;
            }
            else{
                for(int j = 0;j < Z;j++){
                    int v = chd[i][j];
                    if(v){
                        A.m[v][i] += 1.0/6;
                    }
                    else{
                        int t = fail[i];
                        while(t && !chd[t][j]) t = fail[t];
                        v = chd[t][j];
                        A.m[v][i] = 1.0/6;
                    }
                }
            }
        }
    }

    void dfs(int u){
        if(Mark[u]){
            A.m[u][u] = 1.0;
            return ;
        }
        for(int i = 0;i < Z;i++){
            int v = chd[u][i];
            if(v){
                A.m[v][u] = 1.0/6;
                dfs(v);
            }
            else{
                int t = fail[v];
                while(t && !chd[t][i]) t = fail[t];
                t = chd[t][i];
                A.m[t][u] = 1.0/6;
            }
        }
    }
}AC;


int main(){
    //freopen("in.txt","r",stdin);
    int T,n,l;
    scanf("%d",&T);
    while(T--){
        scanf("%d%d",&n,&l);
        AC.Init();
        for(int i = 0;i < n;i++){
            for(int j = 0;j < l;j++){
                scanf("%d",&gue[j]);
            }
            AC.Insert(i,l);
        }
        AC.Build();
        //printf("%d\n",AC.nn);
        matrix x = matrix(AC.nn,1);
        x.m[0][0] = 1;
        //A.print();
        matrix tmp = A.qpow(1ll<<30);
        x = tmp*x;

        double tot = 0;
        for(int i = 0;i < n;i++){
            tot += x.m[AC.fin[i]][0];
        }
        //printf("%f\n",tot);
        for(int i = 0;i < n;i++){
            printf("%.6f",x.m[AC.fin[i]][0]/tot);
            if(i == n-1) printf("\n");
            else printf(" ");
        }
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值