BZOJ 1444: [Jsoi2009]有趣的游戏
一道重拾AC自动机的题,可以看作BZOJ 4820:[Sdoi2017]硬币游戏的弱化版
考虑一个暴力的想法,我们把所有串扔进一个AC自动机里,然后在fail树上跑DP,假设\(f_x\)表示节点\(x\)表示的状态出现的概率(即经过点\(x\)的概率),那么有:
\[f_{ch_{x,i}}=\sum_{i=0}^{m-1} p_i\times f_{x}
\]
由于AC自动机的节点特性,这个转移成环了,因此上一波高斯消元,看似顺利解决?
写完跑一跑发现TM的概率都是\(0\)啊,这是什么鬼?
让我们仔细想一想,初始时经过根节点的概率\(f_0=1\),但是从根节点转移出去之后又有可能走回来,可是根节点的概率最大就是\(1\),这样就没法处理了!
那么这道题我们就不能从概率的角度入手了,关于经过多次的问题我们考虑期望
我们重新令\(f_x\)表示经过节点\(x\)的期望次数,这样有一个显而易见的好处:当我们走到单词的结尾时,游戏直接结束,那么就意味着走到它的期望次数就是走到它的概率
然后我们发现这个时候对于根节点我们就有办法处理了,\(f_0=1+\sum_{i=1}^{tot} f_i\times (\text{i走到0的概率})\),其它的点同理,只是不用加\(1\)罢了
然后再上高消就没有问题了,复杂度\(O((nl)^3)\),可以轻松通过
PS:注意特判所有人都赢不了的情况,否则你就会得到大量的nan
PPS:注意精度,尤其是输出-0.00
的问题
#include<cstdio>
#include<iostream>
#include<cmath>
#define RI register int
#define CI const int&
using namespace std;
const int N=105;
const double EPS=1e-10;
int n,l,m,x,y,cur; char s[N],pos[N]; double g[N],p[N][N],val[N];
inline void Gauss(CI n)
{
RI i,j,k; for (i=0;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];
}
}
class AC_Automation
{
private:
struct ac_node
{
int ch[26],fail;
}node[N]; int tot,q[N]; bool end[N];
#define next(x,y) node[x].ch[y]
#define fail(x) node[x].fail
inline void get_fail(void)
{
RI i,j,H=0,T=0; for (i=0;i<m;++i) if (next(0,i)) q[++T]=next(0,i);
while (H<T)
{
int now=q[++H],to; for (i=0;i<m;++i) if (to=next(now,i))
end[to]|=end[fail(q[++T]=to)=next(fail(now),i)];
else next(now,i)=next(fail(now),i);
}
//for (i=0;i<=tot;++i) printf("%d\n",fail(i));
}
public:
inline void insert(char *s,CI id)
{
int now=0; for (RI i=1;i<=l;++i)
!next(now,s[i]-'A')&&(next(now,s[i]-'A')=++tot),now=next(now,s[i]-'A');
pos[id]=now; end[now]=1;
}
inline void solve(void)
{
get_fail(); for (RI i=0,j;i<=tot;++i)
{
p[i][i]=-1.0; if (end[i]) continue;
for (j=0;j<m;++j) p[next(i,j)][i]+=g[j];
}
val[0]=-1.0; Gauss(tot);
}
#undef next
#undef fail
}AC;
int main()
{
//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
RI i,j; for (scanf("%d%d%d",&n,&l,&m),i=0;i<m;++i)
scanf("%d%d",&x,&y),g[i]=1.0*x/y; for (i=1;i<=n;++i)
{
for (scanf("%s",s+1),AC.insert(s,i),j=1;j<=l;++j)
if (fabs(g[s[j]-'A'])<EPS) { ++cur; break; }
}
if (cur==n) { for (i=1;i<=n;++i) puts("0.00"); return 0; }
for (AC.solve(),i=1;i<=n;++i)
{
if (fabs(val[pos[i]])<EPS) puts("0.00");
else printf("%.2lf\n",val[pos[i]]);
}
return 0;
}
辣鸡老年选手AFO在即