好仙的题目啊,本来是KMP里的题但最后该用的地方被我用Hash艹过去了算了反正这不是这道题的重点
考虑一个暴力的(O((nm)^3))的做法,其实就是BZOJ 1444: [Jsoi2009]有趣的游戏的弱化版,但在这道题中直接上只能得到40pts
我们考虑一下这个方法为什么不行,其实主要的原因是状态实在太多了
考虑到实际上有意义的状态只有(n)个,其余的状态都可以合并成一个——“不合法的状态”
乍一看是不是很乱来?怎么能把那么多不同的状态归为一类?
没事我们来找个情况分析一下,在这之前先放一个显然的引理:
引理:构造一个指定的长度为(l)的(01)串的概率是(frac{1}{2^l})
考虑现在(S)是一个不合法的状态(即没有人获胜),(A=101),(B=110)
那么我们假定游戏进行到到(S+101)的状态,那么此时游戏一定会停止,但不一定要等到(101)加完才停止
很显然我们可以看出当(S)的后缀是(1)或者(10)时游戏会提前结束,换句话说会有这些情况:
其中(S=S'+10=S''+1)
那么我们根据上面的引理,可以得到一个方程:
是不是很神奇,但仔细一想我们发现这确实包含了所有情况,那么我们来试着形式化一下这钟方法:
对于每一个字符串(s_i),设它获胜的概率是(p_i),不合法(即永远无法结束)的辅助状态的概率设为(S)
我们发现对于另一个字符串(s_j),若(s_j)存在长度为(k)的后缀能匹配(s_i)的前缀,那么就有(frac{1}{2^{m-k}})的概率提前结束
那么我们在设(pre_{i,j})表示字符串(s_i)长度为(j)的前缀,(suf_{i,j})表示字符串(s_i)长度为(j)的后缀
那么对于每一个(s_i),我们都将(S+s_i)作为一次终止状态列个方程,即:
但这样只有(n)个方程,但变量有(n+1)个。我们发现其实游戏必然会结束,即(S=0),所以可以列出最后一个方程:(sum_{i=1}^n p_i=1),然后就上高消即可,复杂度(O(n^3))
PS:那个匹配前缀后缀可以用KMP,但我比较懒因此写了Hash
PPS:这题非常开精度,如果你得到了40pts的好成绩请把高消写成精度更高的方法,并且在BZOJ上由于没有SPJ,因此EPS
要调到(10^{-10})
#include<cstdio>
#include<iostream>
#include<cmath>
#define RI register int
#define CI const int&
using namespace std;
typedef unsigned long long u64;
const int N=305;
const double EPS=1e-10;
struct Hasher
{
u64 x,y;
inline Hasher(const u64& X=0,const u64& Y=0) { x=X; y=Y; }
friend inline bool operator == (const Hasher& A,const Hasher& B)
{
return A.x==B.x&&A.y==B.y;
}
friend inline Hasher operator + (const Hasher& A,const Hasher& B)
{
return Hasher(A.x+B.x,A.y+B.y);
}
friend inline Hasher operator * (const Hasher& A,const Hasher& B)
{
return Hasher(A.x*B.x,A.y*B.y);
}
}pw[N],pre[N][N],suf[N][N]; int n,m; double pw2[N],p[N][N],val[N]; char s[N];
const Hasher seed=Hasher(233,167);
inline void Gauss(CI n)
{
RI i,j,k; for (i=1;i<=n;++i)
{
int mx=i; for (j=i;j<=n;++j) if (fabs(p[j][i])>=fabs(p[mx][i]))
mx=j; swap(p[mx],p[i]); swap(val[mx],val[i]);
for (j=i+1;j<=n;++j) if (fabs(p[j][i])>=EPS)
{
double dv=1.0*p[j][i]/p[i][i]; val[j]-=val[i]*dv;
for (k=i;k<=n;++k) p[j][k]-=p[i][k]*dv;
}
}
for (val[n]/=p[n][n],i=n-1;i;--i)
{
for (j=i+1;j<=n;++j) val[i]-=p[i][j]*val[j];
val[i]/=p[i][i];
}
}
int main()
{
RI i,j,k; for (scanf("%d%d",&n,&m),pw[0]=Hasher(1,1),pw2[0]=i=1;i<=m;++i)
pw[i]=pw[i-1]*seed,pw2[i]=pw2[i-1]*0.5; for (i=1;i<=n;++i)
{
for (scanf("%s",s+1),j=1;j<=m;++j) pre[i][j]=(pre[i][j-1]*seed)+Hasher(s[j],s[j]);
for (j=m;j;--j) suf[i][j]=suf[i][j+1]+(Hasher(s[j],s[j])*pw[m-j]);
}
for (i=1;i<=n;++i) for (j=1;j<=n;++j) for (k=1;k<=m;++k)
if (pre[i][k]==suf[j][m-k+1]) p[i][j]+=pw2[m-k];
for (i=1;i<=n;++i) p[i][n+1]=-pw2[m],p[n+1][i]=1; val[n+1]=1;
for (Gauss(n+1),i=1;i<=n;++i) printf("%.10lf
",val[i]);
return 0;
}