zoukankan      html  css  js  c++  java
  • 【BZOJ4820】硬币游戏(SDOI2017)-概率+高斯消元+KMP

    测试地址:硬币游戏
    做法:本题需要用到概率+高斯消元+KMP。
    一看到这题,我们很快想到用JSOI2009-有趣的游戏那题一样,先建AC自动机,然后在AC自动机上建转移图,再高斯消元解方程……但是看到残酷的数据范围,我们就知道我们必须另辟蹊径了。
    因为高斯消元的复杂度已经不能再降了,于是复杂度的瓶颈就在于方程太多了。因为我们要求的是p(si),即以si结尾且仅在字符串结尾匹配上串si的概率,而这样的概率只有n个,那么我们如果令N为其他所有没匹配上的状态,如果我们能列出方程的话,方程的个数就是n+1个,可以接受。
    考虑在一个没匹配到任何串的串N后面接一个串si,那么一旦接完这个游戏就会立刻停止,但是不一定在接完si后游戏才停止,有可能在接完之前先匹配上一个串。也就是说,N的一个后缀和si的一个前缀组成了一个sj。那么这个字符串就可以表示成,N的一个前缀+sj+si的一个后缀。因为N的一个前缀+sj这一部分出现的概率就等于p(sj),而在后面接出一个特定长度l01串的概率是12l,所以我们令si剩下的后缀长度为l,这种情况出现的概率就是12lp(sj)
    那么,对于每个串sj,对于每种它的一个后缀和si的一个前缀的匹配,设匹配长度为l,发生的概率都是12mlp(sj),而对于所有的这些情况,发生的概率总和显然是12mp(N)(即出现N+si这个串的概率),那么我们就可以列出一个方程了。对于每个si,我们都可以列出一个这样的方程,但是现在未知数有n+1个(因为有p(N)),方程只有n个,无法求解。这时候我们发现有一个隐含条件:p(si)=1,把这个当做方程,就可以高斯消元解方程组了,时间复杂度为O(n3)
    现在我们的问题就是求出sjsi方程的贡献,我们其实只要将si作为模式串做KMP,那么最后匹配到的那个前缀以及能通过next指针走到的所有前缀,都是sj的一个后缀,累加贡献即可,时间复杂度为O(n3)
    以下是本人代码:

    #include <bits/stdc++.h>
    using namespace std;
    int n,m,nxt[310];
    char s[310][310];
    long double tot[310],g[310][310]={0},pwr[310];
    
    void build(int i)
    {
        nxt[0]=-1;tot[0]=pwr[m-1];
        int last;
        for(int j=1;j<m;j++)
        {
            last=nxt[j-1];
            while(last!=-1&&s[i][last+1]!=s[i][j]) last=nxt[last];
            if (s[i][last+1]==s[i][j]) nxt[j]=last+1;
            else nxt[j]=last;
            if (s[i][last+1]==s[i][j]) tot[j]=tot[nxt[j]]+pwr[m-j-1];
            else tot[j]=pwr[m-j-1];
        }
    }
    
    void calc(int i,int j)
    {
        int last=-1;
        for(int k=0;k<m;k++)
        {
            while(last!=-1&&s[i][last+1]!=s[j][k]) last=nxt[last];
            if (s[i][last+1]==s[j][k]) last++;
        }
        if (last!=-1) g[i][j]=tot[last];
    }
    
    void gauss(int n)
    {
        for(int i=1;i<=n;i++)
        {
            int mx=i;
            for(int j=i+1;j<=n;j++)
                if (fabs(g[j][i])>fabs(g[mx][i])) mx=j;
            for(int j=i;j<=n+1;j++)
                swap(g[i][j],g[mx][j]);
            for(int j=i+1;j<=n;j++)
            {
                for(int k=i+1;k<=n+1;k++)
                    g[j][k]-=g[j][i]*g[i][k]/g[i][i];
                g[j][i]=0.0;
            }
        }
        for(int i=n;i>=1;i--)
            for(int j=1;j<i;j++)
            {
                g[j][n+1]-=g[j][i]*g[i][n+1]/g[i][i];
                g[j][i]=0.0;
            }
    }
    
    int main()
    {
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++)
            scanf("%s",s[i]);
    
        pwr[0]=1.0;
        for(int i=1;i<=m;i++)
            pwr[i]=pwr[i-1]*0.5;
        for(int i=1;i<=n;i++)
        {
            build(i);
            for(int j=1;j<=n;j++)
                calc(i,j);
        }
        for(int i=1;i<=n;i++)
            g[i][n+1]=-pwr[m];
        for(int i=1;i<=n;i++)
            g[n+1][i]=1.0;
        g[n+1][n+2]=1.0;
    
        gauss(n+1);
        for(int i=1;i<=n;i++)
            printf("%.10Lf
    ",g[i][n+2]/g[i][i]);
    
        return 0;
    }
  • 相关阅读:
    【Nginx 快速入门】反向代理、负载均衡、动静分离
    【Redis】缓存穿透、缓存击穿、缓存雪崩(面试必备)
    【Redis】特殊数据类型
    【Redis】特殊数据类型
    【Redis】特殊数据类型
    typescript 技巧学习
    angular9的学习(十九)
    angular11源码探索二十六[Router整体路由配置]
    angular11源码探索二十五[Router路由事件]
    angular11源码探索二十四[路由检测变更策略]
  • 原文地址:https://www.cnblogs.com/Maxwei-wzj/p/9793461.html
Copyright © 2011-2022 走看看