bzoj 1444 [Jsoi2009]有趣的游戏

题目

好像是\(SDOI2017\)某题的数据范围弱化版

不过把这道题放到\(SDOI2017\)某题应该有\(60\)可以拿吧

看到多串首先建\(AC\)自动机

之后就可以视为把这个随机生成的字符串放到\(AC\)机上匹配,由于是随机生成,那么本质上就是在\(AC\)机上随机游走,走到有结束标记的点就停下来

\(dp[i]\)表示\(i\)点经过的期望次数,由于终点走到就停下了,所以终点的期望等价于概率

非常显然我们可以列出这样的方程

\[dp_v=\sum_{e(u,v)\in G}dp_u\times w(u,v) \]

\(w(u,v)\)就是走这条边的概率

但是\(0\)号节点是特殊的,因为这个节点是起点,期望次数应该加\(1\)

也就是

\[dp_0=1+\sum_{e(u,0)\in G}dp_u\times w(u,v) \]

之后就会发现这样的方程根本没有办法来推,因为\(AC\)机根本就不是一个拓扑结构

所以我们把这些方程一个一个列出来,之后高斯消元来求出每一个\(dp\)的值

复杂度\(O(n^3m^3)\)

代码

#include<iostream>
#include<cstring>
#include<cstdio>
#include<queue>
#include<cmath>
#define eps 1e-6
#define re register
#define maxn 205
#define LL long long
#define inf 999999999
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
inline int read()
{
    char c=getchar();int x=0;while(c<'0'||c>'9') c=getchar();
    while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+c-48,c=getchar();return x;
}
inline int check(double a) {if(std::fabs(a)<eps) return 1;return 0;}
int n,m,len,cnt;
char S[maxn];
double P[maxn];
double a[maxn][maxn],ans[maxn],dp[maxn];
int son[maxn][26],fail[maxn],f[maxn];
inline void ins(int o)
{
    int now=0;
    for(re int i=1;i<=len;i++) 
    {
        if(!son[now][S[i]-'A']) son[now][S[i]-'A']=++cnt;
        now=son[now][S[i]-'A'];
    }
    f[now]=o;
}
inline void build()
{
    std::queue<int> q;
    for(re int i=0;i<m;i++) if(son[0][i]) q.push(son[0][i]);
    while(!q.empty())
    {
        int k=q.front();q.pop();
        for(re int i=0;i<m;i++)
        if(son[k][i]) fail[son[k][i]]=son[fail[k]][i],q.push(son[k][i]);
            else son[k][i]=son[fail[k]][i];
    }
}
int main()
{
    n=read(),len=read(),m=read();
    int p,q;
    for(re int i=0;i<m;i++)
        p=read(),q=read(),P[i]=double(p)/double(q);
    for(re int i=1;i<=n;i++) scanf("%s",S+1),ins(i);
    build();
    for(re int i=0;i<=cnt;i++)
    {
        if(f[i]) continue;
        for(re int j=0;j<m;j++)
            a[son[i][j]][i]+=P[j];
    }
    for(re int i=0;i<=cnt;i++)a[i][i]+=-1;
    a[0][cnt+1]=-1.0;
    for(re int i=0;i<=cnt;i++)
    {
        int now=i;
        for(re int j=i+1;j<=cnt;j++) if(std::fabs(a[j][i])>std::fabs(a[now][i])) now=j;
        std::swap(a[now],a[i]);
        for(re int j=cnt+1;j>=i;--j)
            a[i][j]/=a[i][i];
        for(re int j=i+1;j<=cnt;j++)
            for(re int k=cnt+1;k>=i;--k)
                a[j][k]-=a[j][i]*a[i][k];
    }
    ans[cnt]=a[cnt][cnt+1];
    for(re int i=cnt-1;i>=0;i--)
    {
        ans[i]=a[i][cnt+1];
        for(re int j=i+1;j<=cnt;j++)
            ans[i]-=a[i][j]*ans[j];
    }
    for(re int i=0;i<=cnt;i++) dp[f[i]]=ans[i];
    for(re int i=1;i<=n;i++) 
    {
        if(check(dp[i])) puts("0.00");
        else printf("%.2lf\n",dp[i]);
    }
    return 0;
}
posted @ 2019-01-28 09:50  asuldb  阅读(133)  评论(0编辑  收藏  举报