zoukankan      html  css  js  c++  java
  • 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 imes f_{x} ]

    由于AC自动机的节点特性,这个转移成环了,因此上一波高斯消元,看似顺利解决?

    写完跑一跑发现TM的概率都是(0)啊,这是什么鬼?

    让我们仔细想一想,初始时经过根节点的概率(f_0=1),但是从根节点转移出去之后又有可能走回来,可是根节点的概率最大就是(1),这样就没法处理了!

    那么这道题我们就不能从概率的角度入手了,关于经过多次的问题我们考虑期望

    我们重新令(f_x)表示经过节点(x)的期望次数,这样有一个显而易见的好处:当我们走到单词的结尾时,游戏直接结束,那么就意味着走到它的期望次数就是走到它的概率

    然后我们发现这个时候对于根节点我们就有办法处理了,(f_0=1+sum_{i=1}^{tot} f_i imes ( ext{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
    ",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
    ",val[pos[i]]);
        }
        return 0;
    }
    
  • 相关阅读:
    TensorFlow基础篇
    MySql分类
    VISUAL STUDIO 调试
    排序分类
    位分类
    Visio分类
    工作线程AfxBeginThread的使用
    Windows Live Writer配置步骤
    用户界面线程AfxBeginThread的使用
    WIndows 相关知识
  • 原文地址:https://www.cnblogs.com/cjjsb/p/12246868.html
Copyright © 2011-2022 走看看