• 【BZOJ 2553】[BeiJing2011]禁忌 AC自动机+期望概率dp


    我一开始想的是倒着来,发现太屎,后来想到了一种神奇的方法——我们带着一个既有期望又有概率的矩阵,偶数(2*id)代表期望,奇数(2*id+1)代表概率,初始答案矩阵一列,1的位置为1(起点为0),工具矩阵上如果是直接转移那么就是由i到j概率期望都乘上1/alphabet,特别的,对于一个包含禁忌串的节点直接由其父节点指向0,而且在计算期望是多加上他的概率,最后统计答案时把答案矩阵上所有的期望加和即可,这个方法很完美的被卡精了.......

    #include <cstdio>
    #include <cstring>
    #include <vector>
    typedef long double LD;
    const int N=200;
    char s[20];
    int alpha,n,m;
    struct Trie{
      int ch[26],fail,deep;
      bool god;
    }node[N];
    int sz,q[N],size;
    LD a[N][N],temp_a[N][N],b[N],temp_b[N],need;
    inline void insert(char *w){
      int p=0;
      for(int i=0;w[i];i++){
        if(node[p].ch[w[i]-'a']==0)node[p].ch[w[i]-'a']=++sz,node[sz].deep=node[p].deep+1;
        p=node[p].ch[w[i]-'a'];
      }
      node[p].god=1;
    }
    inline void build(){
      q[0]=0;
      for(int i=0,j=0;i<=j;i++){
        for(int l=0;l<alpha;l++)
          if(node[q[i]].ch[l])
            q[++j]=node[q[i]].ch[l],
            node[q[j]].fail=q[i]?node[node[q[i]].fail].ch[l]:0,
            node[q[j]].god=node[q[j]].god||node[node[q[j]].fail].god;
          else
            node[q[i]].ch[l]=q[i]?node[node[q[i]].fail].ch[l]:0;
      }
    }
    void dfs(int x){
        for(int i=0;i<alpha;i++){
            if(node[node[x].ch[i]].god){
                a[0][x<<1]+=need;
                a[1][(x<<1)+1]+=need;
                a[0][(x<<1)+1]+=need;
                continue;
            }
            a[node[x].ch[i]<<1][x<<1]+=need;
            a[(node[x].ch[i]<<1)+1][(x<<1)+1]+=need;
            if(node[node[x].ch[i]].deep>node[x].deep)
                dfs(node[x].ch[i]);
        }
    }
    inline void Multi_a(){
      for(int i=0;i<=size;i++)
        for(int j=0;j<=size;j++)
          temp_a[i][j]=0;
      for(int i=0;i<=size;i++)
        for(int j=0;j<=size;j++)
          for(int k=0;k<=size;k++)
            temp_a[i][j]+=a[i][k]*a[k][j];
      for(int i=0;i<=size;i++)
        for(int j=0;j<=size;j++)
          a[i][j]=temp_a[i][j];
    }
    inline void Multi_b(){
      for(int i=0;i<=size;i++)temp_b[i]=0;
      for(int i=0;i<=size;i++)
        for(int j=0;j<=size;j++)
          temp_b[i]+=a[i][j]*b[j];
      for(int i=0;i<=size;i++)b[i]=temp_b[i];
    }
    int main(){
      scanf("%d%d%d",&n,&m,&alpha),need=1./alpha;
      for(int i=1;i<=n;i++)
        scanf("%s",s),insert(s);
      build(),dfs(0),size=(sz<<1)+1,b[1]=1.;
      while(m){
        if(m&1)Multi_b();
        m>>=1,Multi_a();
      }
      LD ans=0.;
      for(int i=0;i<=sz;i++)ans+=b[i<<1];
      printf("%lf",(double)ans);
      return 0;
    }
    只是错了最后一个点

    然后我去接受正解,按照我们的思路我们会建出来一个概率矩阵a[i][j]表示由j转移到i的概率,那就是加上1/alphabet,当然这里也有从禁忌串直接调回0的操作,然后我们想记录答案,我们就把矩阵扩大一阶,标号为sz+1,就是记录答案,那么我们由禁忌串向sz+1加上1/alphabet,那么我们发现这个矩阵自乘多少次,他的a[sz+1][0]的答案就是我们想要的答案。

    经验教训:对于矩阵乘法,答案矩阵也可以是方阵,而且在一个矩阵里意义可以不同。

    #include <cstdio>
    #include <cstring>
    #include <vector>
    typedef long double LD;
    const int N=100;
    char s[20];
    int alpha,n,m;
    struct Trie{
      int ch[26],fail,deep;
      bool god;
    }node[N];
    int sz,q[N],size;
    LD a[N][N],temp[N][N],b[N][N],need;
    inline void insert(char *w){
      int p=0;
      for(int i=0;w[i];i++){
        if(node[p].ch[w[i]-'a']==0)node[p].ch[w[i]-'a']=++sz,node[sz].deep=node[p].deep+1;
        p=node[p].ch[w[i]-'a'];
      }
      node[p].god=1;
    }
    inline void build(){
      q[0]=0;
      for(int i=0,j=0;i<=j;i++){
        for(int l=0;l<alpha;l++)
          if(node[q[i]].ch[l])
            q[++j]=node[q[i]].ch[l],
            node[q[j]].fail=q[i]?node[node[q[i]].fail].ch[l]:0,
            node[q[j]].god=node[q[j]].god||node[node[q[j]].fail].god;
          else
            node[q[i]].ch[l]=q[i]?node[node[q[i]].fail].ch[l]:0;
      }
    }
    void dfs(int x){
        for(int i=0;i<alpha;i++){
            if(node[node[x].ch[i]].god){
                a[0][x]+=need;
                a[sz+1][x]+=need;
                continue;
            }
            a[node[x].ch[i]][x]+=need;
            if(node[node[x].ch[i]].deep>node[x].deep)
                dfs(node[x].ch[i]);
        }
    }
    inline void Multi_a(){
      for(int i=0;i<=size;i++)
        for(int j=0;j<=size;j++)
          temp[i][j]=0;
      for(int i=0;i<=size;i++)
        for(int j=0;j<=size;j++)
          for(int k=0;k<=size;k++)
            temp[i][j]+=a[i][k]*a[k][j];
      for(int i=0;i<=size;i++)
        for(int j=0;j<=size;j++)
          a[i][j]=temp[i][j];
    }
    inline void Multi_b(){
      for(int i=0;i<=size;i++)
        for(int j=0;j<=size;j++)
          temp[i][j]=0;
      for(int i=0;i<=size;i++)
        for(int j=0;j<=size;j++)
          for(int k=0;k<=size;k++)
            temp[i][j]+=b[i][k]*a[k][j];
      for(int i=0;i<=size;i++)
        for(int j=0;j<=size;j++)
          b[i][j]=temp[i][j];
    }
    int main(){
      scanf("%d%d%d",&n,&m,&alpha),need=(LD)1./(LD)alpha;
      for(int i=1;i<=n;i++)
        scanf("%s",s),insert(s);
      build(),dfs(0),size=sz+1,a[size][size]=1.;
      for(int i=0;i<=size;i++)b[i][i]=1.;
      while(m){
        if(m&1)Multi_b();
        m>>=1,Multi_a();
      }
      printf("%.6lf",(double)b[size][0]);
      return 0;
    }
    AC code
  • 相关阅读:
    NYOJ 23 取石子(一)
    XYNUOJ 2026 素数环
    XYNUOJ 1756 魔法工会
    XYNUOJ 1784 胜利大逃亡
    NYOJ 18 The Triangle
    NYOJ 737 合并石子
    XYNUOJ 问题 B: 敌兵布阵
    NYOJ 1063 生活的烦恼
    XYNUOJ 1774 最少拦截系统
    XYNUOJ 1248 排队打水问题
  • 原文地址:https://www.cnblogs.com/TSHugh/p/7528564.html
Copyright © 2020-2023  润新知