洛谷 P3706
Posted None
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了洛谷 P3706相关的知识,希望对你有一定的参考价值。
听说是 PGF 板板题,但是不会 PGF,怎么办捏(
暴力做法显然是建出 AC 自动机但是高斯消元,但是状态数高达 \\(nm\\),有没有优化的余地呢?
注意到终止状态只有 \\(n\\) 个,AC 自动机上其他节点表示的状态其实都可以归结为“非终止状态”,因此我们考虑设 \\(n\\) 个变量 \\(x_1\\sim x_n\\) 表示最先出现字符串 \\(s_i\\) 的概率,和一个辅助变量 \\(x_n+1\\) 表示当前位于不合法状态的概率(当然如果将这个解释为“概率”其实有点牵强,因为我们设立这个变量并不是为了解出 \\(x_n+1\\) 这个没啥实际意义的东西,而是计算出 \\(x_1\\sim x_n\\) 之间的比例关系,然后根据 \\(\\sum x_i=1\\) 解出所有 \\(x_i\\),但是其实我也找不到更好的解释方式,只好将这个东西用”概率“来解释)
考虑如何建立这 \\(n+1\\) 个变量之间的方程,对于每个字符串 \\(i\\),我们考虑现在我们在不合法状态,然后以 \\(2^-m\\) 的概率恰好在后面添加了字符串 \\(s_i\\),那么这个概率应该用什么来衡量呢?你可能说是 \\(x_i\\),但是问题在于,不一定非得添加完全部 \\(m\\) 个字符才能第一次得到 \\(s_i\\),比方说 \\(s_i=101\\),然后现在字符串末尾刚好有个 \\(10\\),这样添加一个 \\(1\\) 以后就到了 \\(x_i\\) 的状态。换句话说,添加完 \\(m\\) 个字符以后恰好第一次出现 \\(s_i\\) 的概率 \\(x_i\\) 只是 \\(x_n+1·2^-m\\) 的一部分。为了覆盖所有的情况,我们考虑所有字符串 \\(s_j\\)(当然,\\(j\\) 也可以等于 \\(i\\)),如果 \\(s_i\\) 长度为 \\(k\\) 的前缀与 \\(s_j\\) 长度为 \\(k\\) 的后缀完全相等,说明有可能是从 \\(x_k\\) 的状态以 \\(2^-(m-k)\\) 的概率添加了 \\(s_i\\) 的后 \\(m-k\\) 的字符得到当前状态的,因此我们有
当然最后还有个 \\(\\sum x_i=1\\)。直接高斯消元即可。
有点卡精度,很不明白为什么不取模,有点复古。下头/ruo
const int MAXN=300;
const u64 BS=131;
const ld EPS=1e-10;
int n,m;char s[MAXN+5][MAXN+5];
u64 pw_hsh[MAXN+5],pre[MAXN+5][MAXN+5],suf[MAXN+5][MAXN+5];
ld a[MAXN+5][MAXN+5],pw2[MAXN+5],res[MAXN+5];
int main()
scanf("%d%d",&n,&m);
for(int i=(pw_hsh[0]=pw2[0]=1);i<=m;i++)pw_hsh[i]=pw_hsh[i-1]*BS,pw2[i]=pw2[i-1]*0.5;
for(int i=1;i<=n;i++)scanf("%s",s[i]+1);
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)pre[i][j]=pre[i][j-1]*BS+s[i][j];
for(int j=m;j;j--)suf[i][j]=suf[i][j+1]+pw_hsh[m-j]*s[i][j];
for(int i=1;i<=n;i++)
a[i][n+1]=-pw2[m];
for(int j=1;j<=n;j++)for(int k=1;k<=m;k++)
if(suf[j][m-k+1]==pre[i][k])a[i][j]+=pw2[m-k];
for(int i=1;i<=n;i++)a[n+1][i]=1;a[n+1][n+2]=1;
for(int i=1;i<=n+1;i++)
int t=i;
for(int j=i+1;j<=n+1;j++)if(fabs(a[j][i]-a[t][i])>EPS)t=j;
for(int j=i;j<=n+2;j++)swap(a[i][j],a[t][j]);
for(int j=i+1;j<=n+2;j++)a[i][j]/=a[i][i];a[i][i]=1;
for(int j=i+1;j<=n+1;j++)
for(int k=i+1;k<=n+2;k++)a[j][k]-=a[j][i]*a[i][k];
a[j][i]=0;
for(int i=n+1;i;i--)
res[i]=a[i][n+2];
for(int j=i+1;j<=n+1;j++)res[i]-=res[j]*a[i][j];
for(int i=1;i<=n;i++)printf("%.10Lf\\n",res[i]);
return 0;
Codevs 1497 取余运算== 洛谷P 1226
以上是关于洛谷 P3706的主要内容,如果未能解决你的问题,请参考以下文章