BZOJ 1444 [JSOI2009]有趣的游戏 (Trie图/AC自动机+矩阵求逆)

题目大意:给你$N$个长度相等且互不相同的模式串,现在有一个字符串生成器会不断生成字符,其中每个字符出现的概率是$p_{i}/q_{i}$,当生成器生成的字符串包含了某个模式串,则拥有该模式串的玩家胜利,然后游戏立即结束,求每个玩家获胜的概率 $N<=10$

首先建出$Trie$图

接着设$f[i]$表示匹配时停在i的概率,可得$f[ch{k}]+=f[i]*p_{k}/q_{k}$

由于$N$很小,可以构建$dp$转移的邻接矩阵,由于生成器生成的串是无限长的,相当于把矩阵乘了无限次幂

可以耍赖一点...把矩阵自乘很多次,反正是保留小数卡精度过

正确的做法呢,就是利用等比数列求极限的方法,即$1/(1-p)$,1在这里是单位矩阵,$p$是邻接矩阵

然后对$(1-p)$这个矩阵求逆即可

  1 #include <cmath>
  2 #include <queue>
  3 #include <vector>
  4 #include <cstdio>
  5 #include <cstring>
  6 #include <algorithm>
  7 #define NN 105
  8 #define maxn 100000
  9 #define ll long long
 10 #define dd double  
 11 #define uint unsigned int
 12 #define mod 1000000007
 13 #define idx(X) (X-'A')
 14 #define eps (1e-9)
 15 using namespace std;
 16 
 17 int n,m;
 18 int ed[NN];
 19 int p[20],q[20],L,num;
 20 
 21 struct M{
 22 dd f[NN][NN*2];
 23 friend M operator * (const M &a,const M &b){
 24     M ret;memset(&ret,0,sizeof(ret));
 25     for(int i=0;i<n;i++)
 26         for(int j=0;j<n;j++)
 27             for(int k=0;k<n;k++)
 28             ret.f[i][j]+=a.f[i][k]*b.f[k][j];
 29     return ret;
 30 }
 31 int Gauss()
 32 {
 33     int nn=n*2;
 34     for(int i=0;i<n;i++)
 35         f[i][i+n]=1;
 36     for(int i=0;i<n;i++)
 37     {
 38         for(int j=i;j<n;j++)
 39         if(fabs(f[j][i])>eps){
 40             for(int k=0;k<nn;k++)
 41                 swap(f[i][k],f[j][k]);
 42             break;
 43         }
 44         if(fabs(f[i][i])<eps) return 0;
 45         dd r=1.0/f[i][i];
 46         for(int j=i;j<nn;j++)
 47             f[i][j]*=r;
 48         for(int j=0;j<n;j++)
 49         if(j!=i){
 50             r=f[j][i];
 51             for(int k=i;k<nn;k++)
 52                 f[j][k]=f[j][k]-r*f[i][k];
 53         }
 54     }
 55     for(int i=0;i<n;i++)
 56         for(int j=0;j<n;j++)
 57             f[i][j]=f[i][j+n];
 58     return 1;
 59 }
 60 };
 61 
 62 struct AC{
 63 int ch[NN][26],fail[NN],tot,win[NN];
 64 void Build_Trie(char *str,int len,int id)
 65 {
 66     int x=0;
 67     for(int i=1;i<=len;i++){
 68         if(!ch[x][idx(str[i])])
 69             ch[x][idx(str[i])]=++tot;
 70         x=ch[x][idx(str[i])];
 71     }ed[id]=x;win[x]=1;
 72 }
 73 void Build_Fail()
 74 {
 75     queue<int>q;
 76     for(int i=0;i<m;i++)
 77         if(ch[0][i]) q.push(ch[0][i]);
 78     while(!q.empty())
 79     {
 80         int x=q.front();q.pop();
 81         for(int i=0;i<m;i++)
 82         {
 83             if(ch[x][i]){
 84                 fail[ch[x][i]]=ch[fail[x]][i];
 85                 q.push(ch[x][i]);
 86             }else{
 87                 ch[x][i]=ch[fail[x]][i];
 88             }
 89         }
 90     }
 91 }
 92 void Build_Martix(M &S)
 93 {
 94     for(int x=0;x<=tot;x++)
 95         if(!win[x]){
 96             for(int i=0;i<m;i++)
 97             S.f[ch[x][i]][x]+=1.0*p[i]/q[i];
 98         }
 99     for(int i=0;i<n;i++)
100         for(int j=0;j<n;j++)
101         if(i!=j) S.f[i][j]=0-S.f[i][j];
102         else S.f[i][j]=1.0-S.f[i][j];
103 }
104 }ac;
105 M ans,ni;
106 
107 int main()
108 {
109     //freopen("t1.in","r",stdin);
110     scanf("%d%d%d",&num,&L,&m);
111     for(int i=0;i<m;i++)
112         scanf("%d%d",&p[i],&q[i]);
113     char str[20];
114     for(int i=1;i<=num;i++){
115         scanf("%s",str+1);
116         ac.Build_Trie(str,L,i);}
117     ac.Build_Fail();
118     n=ac.tot+1;
119     ac.Build_Martix(ans);
120     ans.Gauss();
121     for(int i=1;i<=num;i++)
122         printf("%.2lf
",ans.f[ed[i]][0]);
123     return 0;
124 }
原文地址:https://www.cnblogs.com/guapisolo/p/10015714.html