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)=\prod\limits_{x\in S}P_x\)
那么我们有

\[\begin{aligned} \sum\limits_{i=1}^nF_i(x)+G(x)&=1+xG(x)\\ \forall i\in[1,n]\qquad G(x)P(s_i)x^{len_i}&=\sum\limits_{j=1}^n\sum\limits_{k=1}^{len_i}a_{i,j,k}F_j(x)P(s_i[k+1,len_i])x^{len_i-k} \end{aligned} \]

我们要求的是\(\sum\limits_{i=1}^nF_i'(1)\),将上述等式变形可以得到

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

同时我们还有\(\sum\limits_{i=1}^nF_i(1)=1\)
然后利用Gauss消元法求解即可。
\(\sum\limits_{k=1}^{len_i}\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\n",a[i][n+1]);
}

猜你喜欢

转载自www.cnblogs.com/cjoierShiina-Mashiro/p/12977009.html