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\n",ans.f[ed[i]][0]); 123 return 0; 124 }