Luogu P3706 [SDOI2017]硬币游戏

Link 设扔出$i$的概率为$P_i$。 设$f_{i,j}$为首次出现的是$s_i$且随机序列长度为$j$的概率,其OGF为$F_i(x)$。 设$g_i$表示随机序列长度达到$i$且尚未结束的概率,其OGF为$G(x)$。 设$a_{i,j,k}=[pre(s_i,k)=suf(s_j,k)],P(S)=prodlimits_{xin S}P_x$。 那么我们有

[ egin{aligned} sumlimits_{i=1}^nF_i(x)+G(x)&=1+xG(x)\ forall iin[1,n]qquad G(x)P(s_i)x^{len_i}&=sumlimits_{j=1}^nsumlimits_{k=1}^{len_i}a_{i,j,k}F_j(x)P(s_i[k+1,len_i])x^{len_i-k} end{aligned} ]

我们要求的是$sumlimits_^nF_i'(1)$,将上述等式变形可以得到

[ egin{aligned} sumlimits_{i=1}^nF_i'(1)&=G(1)\ forall iin[1,n]qquad G(1)&=sumlimits_{j=1}^nF_j(1)sumlimits_{k=1}^{len_i}frac{a_{i,j,k}}{P(pre(s_i,k))} end{aligned} ]

同时我们还有$sumlimits_nF_i(1)=1$。 然后利用Gauss消元法求解即可。 求$sumlimits_frac{a_{i,j,k}}{P(pre(s_i,k))}$可以使用hash、KMP、AC自动机等方法。

#include<cmath>
#include<cstdio>
#include<algorithm>
using ld=long double;
const int N=307;
int n,m,next[N];char s[N][N];ld a[N][N];
int main()
{
    scanf("%d%d",&n,&m),a[n][n+1]=1,next[0]=-1;
    for(int i=0;i<n;++i) scanf("%s",s[i]);
    for(int i=0;i<n;++i)
    {
	for(int j=0,k=-1;j<m;next[++j]=++k) while(~k&&s[i][j]!=s[i][k]) k=next[k];
	for(int j=0;j<n;++j)
	{
	    int k=0;
	    for(int l=0;l<m;++l,++k) while(~k&&s[j][l]!=s[i][k]) k=next[k];
	    for(;k;k=next[k]) a[i][j]+=pow((ld)0.5,m-k);
	}
	a[i][n]=-pow((ld)0.5,m),a[n][i]=1;
    }
    for(int i=0;i<=n;++i)
    {
	int p=i;
	for(int j=i+1;j<=n;++j) if(fabs(a[j][i])>fabs(a[p][i])) p=j;
	std::swap(a[i],a[p]);
	for(int j=n+i;j>=i;--j) a[i][j]/=a[i][i];
	for(int j=0;j<=n;++j) if(i!=j) for(int k=n+1;k>=i;--k) a[j][k]-=a[j][i]*a[i][k];
    }
    for(int i=0;i<n;++i) printf("%.12Lf
",a[i][n+1]);
}
原文地址:https://www.cnblogs.com/cjoierShiina-Mashiro/p/12977009.html