1. 程式人生 > >HDU 5955 gauss概率dp+ac自動機

HDU 5955 gauss概率dp+ac自動機

題意:有n個人,每個人猜一個[1,6]長度為L的序列,然後無限制的擲骰子,先擲到的那個序列,那個人就是winner,

問每個人贏得概率。

思路:骰子是擲無限次的,但是有許多狀態是重複的,每一種擲法最終必定是以某一個人的序列結束的,

也就是說,誰都不贏的序列是無窮的,概率趨近於0,所以每個人概率相加是1.

我們可以用ac自動機節點編號當做某種情況的狀態,nxt陣列就是這個狀態轉向的下個狀態。

這樣構造出轉移方程就是

dp[i]=sum(dp[from]); dp[i]是i狀態的概率。

最多100個狀態,gauss求解。

注意,終止態不轉移到任何節點

#include<bits/stdc++.h>
using namespace std;

int nxt[500010][7], fail[500010], ed[500010], L, id[110];
struct Trie{
    int root;
    int newnode(){
        for(int i=1; i<=6; i++)
            nxt[L][i]=-1;
        ed[L++]=0;
        return L-1;
    }

    void init(){
        L=0;
        root=newnode();
    }
    void Insert(int buf[], int ii, int len){
        int now=root;
        for(int i=0; i<len; i++){
            int v=buf[i];
            if(nxt[now][v]==-1)
                nxt[now][v]=newnode();
            now=nxt[now][v];
        }
        id[ii]=now;
        ed[now]++;
    }
    void build(){
        queue<int> Q;
        fail[root]=root;
        for(int i=1; i<=6; i++){
            if(nxt[root][i]==-1)
                nxt[root][i]=root;
            else{
                fail[nxt[root][i]]=root;
                Q.push(nxt[root][i]);
            }
        }
        while(!Q.empty()){
            int now=Q.front();
            Q.pop();
            for(int i=1; i<=6; i++)
                if(nxt[now][i]==-1)
                    nxt[now][i]=nxt[fail[now]][i];
                else{
                    fail[nxt[now][i]]=nxt[fail[now]][i];
                    Q.push(nxt[now][i]);
                }
        }
    }
}ac;
const int N=110;
double a[N][N], x[N];
int equ, var;
const double eps=1e-10;


int gauss(){
    int i, j, k, col, max_r;
    for(k=0, col=0; k<equ&&col<var; k++, col++){
        max_r=k;
        for(i=k+1; i<equ; i++)
            if(fabs(a[i][col])>fabs(a[max_r][col]))
                max_r=i;

        if(fabs(a[max_r][col])<eps) return 0;
        if(k!=max_r){
            for(j=col; j<var; j++)
                swap(a[k][j], a[max_r][j]);
            swap(x[k], x[max_r]);
        }
        x[k]/=a[k][col];
        for(j=col+1; j<var; j++) a[k][j]/=a[k][col];
        a[k][col]=1;
        for(i=0;i<equ; i++)
        if(i!=k){
            x[i]-=x[k]*a[i][col];
            for(j=col+1; j<var; j++) a[i][j]-=a[k][j]*a[i][col];
            a[i][col]=0;
        }
    }
    return 1;
}
int s[20];

int main(){
    int T, n, m;
    scanf("%d", &T);
    while(T--){
        memset(x, 0, sizeof x);
        memset(a, 0, sizeof a);
        scanf("%d%d", &n, &m);
        ac.init();
        for(int i=1; i<=n; i++){
            for(int j=0; j<m; j++)
                scanf("%d", s+j);
            ac.Insert(s, i, m);
        }
        ac.build();
        x[0]=-1;
        for(int i=0; i<L; i++){
            a[i][i]=-1;
            if(ed[i]) continue;
            for(int j=1; j<=6; j++){
                a[nxt[i][j]][i]+=1./6;
            }
        }
        var=equ=L;
        gauss();
        for(int i=1; i<n; i++){
            printf("%.6lf ", x[id[i]]);
        }
        printf("%.6lf\n", x[id[n]]);
    }
    return 0;
}