解题:SDOI 2017 硬币游戏

题面

板板的生成函数做法太神仙了,我跑了

朴素的做法是建立AC自动机变成图上的随机游走问题

来仔细考虑一下转移,把状态分成非结尾状态和结尾状态。在一个非结尾状态后补一个串是一定能到达目标串的,但是如果中间出现了前缀等于后缀的情况也可能直接转移到另一个结尾状态。那么我们就用KMP把串之间两两的前缀=后缀的情况状态统计起来列方程,设$p_i$是第$i$个串第一个出现的概率,那么有

$p_i=$

 1 #include<cmath>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<algorithm>
 5 #define double long double
 6 using namespace std;
 7 const int N=330;
 8 const double eps=1e-11;
 9 double pw[N],equ[N][N];
10 int n,m,nxt[N][N]; char str[N][N]; 
11 double Calc(int a,int b)
12 {
13     double ret=0; int o=0;
14     for(int i=0;i<m;o+=str[b][i]==str[a][o],i++)
15         while(o&&str[b][i]!=str[a][o]) o=nxt[a][o];
16     if(a==b) o=nxt[a][o];
17     while(o) ret+=pw[m-o],o=nxt[a][o];
18     return ret;
19 }
20 void Guass()
21 {
22     for(int i=1;i<=n;i++)
23     {
24         int tmp=i;
25         for(int j=i+1;j<=n;j++)    
26             if(fabs(equ[j][i])>fabs(equ[tmp][i])) tmp=j;
27         for(int j=i;j<=n+1;j++)
28             swap(equ[i][j],equ[tmp][j]);
29         for(int j=1;j<=n;j++)
30             if(i!=j)
31             {
32                 double tep=equ[j][i]/equ[i][i];
33                 for(int k=i;k<=n+1;k++)
34                     equ[j][k]-=tep*equ[i][k];
35             }
36     } 
37     for(int i=1;i<=n;i++) equ[i][n+1]/=equ[i][i],equ[i][i]=1;
38 }
39 int main()
40 {
41     scanf("%d%d",&n,&m);
42     for(int i=1;i<=n;i++)
43     {
44         scanf("%s",str[i]);
45         for(int j=1,o=0;j<m;j++)
46         {
47             while(o&&str[i][o]!=str[i][j]) o=nxt[i][o];
48             nxt[i][j+1]=(str[i][o]==str[i][j])?++o:0;
49         }
50     }
51     pw[0]=1;
52     for(int i=1;i<=300;i++) pw[i]=pw[i-1]*0.5;
53     for(int i=1;i<=n;i++)
54         for(int j=1;j<=n;j++)
55             equ[i][j]=Calc(i,j);
56     for(int i=1;i<=n;i++)
57         equ[i][n+1]=-pw[m],equ[i][i]+=1;
58     for(int i=1;i<=n;i++) equ[n+1][i]=1; 
59 //    for(int i=1;i<=n;puts(""),i++)
60 //        for(int j=1;j<=n+1;j++) printf("%.2Lf ",equ[i][j])
61     n++,equ[n][n+1]=1,Guass(); 
62     for(int i=1;i<n;i++) printf("%.10Lf\n",equ[i][n+1]);
63     return 0;
64 } 
View Code

猜你喜欢

转载自www.cnblogs.com/ydnhaha/p/10433202.html