bzoj1444 [Jsoi2009]有趣的游戏
这个题是一道AC自动机上的概率DP,可以帮助理解一下AC自动机的结构。
首先我们把所有的串插入trie,建立AC自动机,求一下转移函数(相当于构造一个trie图),那么就可以概率DP了。首先到达每个点的概率为变量,然后我们要求的就是到达接受状态(也就是每个串的结尾对应的状态)的概率。注意到了接受状态就不必往下转移了,因为游戏就此结束了。注意有一个限制是必须要加的,就是所有接受状态的概率和是1。如果不加的话,会得到所有点的概率等于0这个答案。那么我们就随便拿一个方程替换这个方程就好了。
1 #include<iostream> 2 #include<cstdio> 3 #include<algorithm> 4 #include<cmath> 5 #include<cstring> 6 #define maxn 1200 7 using namespace std; 8 int ch[maxn][12],tran[maxn][12],pre[maxn],q[maxn],v[maxn],d[maxn]; 9 char s[maxn]; 10 double rate[maxn],a[maxn][maxn],ans[maxn]; 11 int n,m,len,num,now,rot; 12 13 void insert(int w) 14 { 15 if (ch[now][w]==0) ch[now][w]=++num; 16 now=ch[now][w]; 17 } 18 19 void build() 20 { 21 int head=0,tail=1; 22 q[1]=rot; 23 while (head<tail) 24 { 25 int now=q[++head]; 26 for (int w=1;w<=m;w++) 27 if (ch[now][w]) 28 { 29 int k=ch[now][w]; 30 if (now==rot) pre[k]=rot; 31 else 32 { 33 int p=pre[now]; 34 while (p&&ch[p][w]==0) p=pre[p]; 35 if (p) pre[k]=ch[p][w]; 36 else pre[k]=rot; 37 } 38 q[++tail]=k; 39 } 40 } 41 } 42 43 void gauss(int n) 44 { 45 int k=1; 46 for (int i=1;i<=n;i++) 47 { 48 int p=0; 49 for (int j=k;j<=n;j++) 50 if (a[j][i]) { p=j; break; } 51 if (!p) continue; 52 for (int l=1;l<=n+1;l++) swap(a[p][l],a[k][l]); 53 for (int j=k+1;j<=n;j++) 54 if (a[j][i]) 55 { 56 double tmp=a[j][i]/a[k][i]; 57 for (int l=1;l<=n+1;l++) a[j][l]-=tmp*a[k][l]; 58 } 59 k++; 60 } 61 for (int i=n;i;i--) 62 { 63 ans[i]=a[i][n+1]; 64 for (int j=i+1;j<=n;j++) 65 ans[i]-=a[i][j]*ans[j]; 66 ans[i]/=a[i][i]; 67 } 68 } 69 70 int main() 71 { 72 //freopen("game.in","r",stdin); 73 //freopen("game.out","w",stdout); 74 scanf("%d%d%d",&n,&len,&m); 75 int x,y; 76 for (int i=1;i<=m;i++) 77 { 78 scanf("%d%d",&x,&y); 79 rate[i]=(y)?(1.0*x/y):0; 80 } 81 now=rot=num=1; 82 for (int i=1;i<=n;i++) 83 { 84 now=rot; 85 scanf("%s",s); 86 for (int j=0;j<len;j++) insert(s[j]-'A'+1); 87 v[now]=i; 88 d[i]=now; 89 } 90 build(); 91 for (int i=1;i<=num;i++) 92 for (int w=1;w<=m;w++) 93 { 94 int p=i; 95 while (p&&ch[p][w]==0) p=pre[p]; 96 if (p) tran[i][w]=ch[p][w]; 97 else tran[i][w]=rot; 98 } 99 //求转移函数 100 for (int i=1;i<=num;i++) 101 { 102 a[i][i]=-1; 103 for (int w=1;w<=m;w++) 104 if (!v[i]) a[tran[i][w]][i]+=rate[w]; 105 } 106 for (int i=1;i<=num;i++) if (v[i]) a[1][i]=1;else a[1][i]=0; 107 a[1][num+1]=1; 108 gauss(num); 109 for (int i=1;i<=n;i++) 110 if (ans[d[i]]>0&&ans[d[i]]<=1) 111 printf("%.2lf\n",ans[d[i]]); 112 else printf("0.00\n"); 113 return 0; 114 }
其实直接把trie图构出来会方便很多吧。
AC without art, no better than WA !