[BZOJ4820][SDOI2017]硬币游戏(高斯消元+KMP)

比较神的一道题,正解比较难以理解。

首先不难得出一个(nm)^3的算法,对所有串建AC自动机,将在每个点停止的概率作为未知数做高斯消元即可。

可以证明,AC自动机上所有不是模式串终止节点的点可以看成一个点,不妨合并为同一个点(n+1号点)。

对于一个模式串,它肯定是从n+1号点走了m步后到达的,概率为0.5^m。但是有可能还没走到m步的时候就已经匹配上另一个串了(可能是它本身),那么这另一个串的后缀一定是这个串的前缀。枚举这种串的位置,将它的概率贡献减去。

这样就是单模式串匹配问题了,可以用KMP解决。现在已经有n个方程了,再加上p[1]+p[2]+...+p[n+1]=1一共n+1个方程,高斯消元解出p[1],...,p[n+1]这n+1个未知数即可。

 1 #include<cmath>
 2 #include<cstdio>
 3 #include<algorithm>
 4 #define rep(i,l,r) for (int i=(l); i<=(r); i++)
 5 using namespace std;
 6 
 7 const int N=310;
 8 int n,m,s[N][N],nxt[N][N];
 9 double a[N][N];
10 
11 void Gauss(int n){
12     rep(i,1,n){
13         int k=i;
14         rep(j,i+1,n) if (fabs(a[j][i])>fabs(a[k][i])) k=j;
15         swap(a[i],a[k]);
16         rep(j,1,n) if (i!=j){
17             double t=a[j][i]/a[i][i];
18             rep(k,1,n+1) a[j][k]-=t*a[i][k];
19         }
20     }
21 }
22 
23 int main(){
24     freopen("bzoj4820.in","r",stdin);
25     freopen("bzoj4820.out","w",stdout);
26     scanf("%d%d",&n,&m); char ch;
27     rep(i,1,n) rep(j,1,m) scanf(" %c",&ch),s[i][j]=(ch=='T');
28     rep(i,1,n){
29         nxt[i][0]=nxt[i][1]=0; int p=0;
30         rep(j,2,m){
31             while (p && s[i][p+1]!=s[i][j]) p=nxt[i][p];
32             if (s[i][p+1]==s[i][j]) nxt[i][j]=++p; else nxt[i][j]=0;
33         }
34     }
35     rep(i,1,n){
36         a[i][i]=-1; a[i][n+1]=pow(0.5,m);
37         rep(j,1,n){
38             int p=0;
39             rep(k,1,m){
40                 while (p && s[i][p+1]!=s[j][k]) p=nxt[i][p];
41                 if (s[i][p+1]==s[j][k]) p++;
42             }
43             if (p==m) p=nxt[i][p];
44             while (p) a[i][j]-=pow(0.5,m-p),p=nxt[i][p];
45         }
46     }
47     rep(i,1,n) a[n+1][i]=1; a[n+1][n+2]=1; Gauss(n+1);
48     rep(i,1,n) printf("%.10lf\n",a[i][n+2]/a[i][i]);
49     return 0;
50 }

 

posted @ 2018-12-14 19:18  HocRiser  阅读(215)  评论(0编辑  收藏  举报