• 【Codeforces 506E】Mr.Kitayuta’s Gift&&【BZOJ 4214】黄昏下的礼物 dp转有限状态自动机+矩阵乘法优化


    神题……
    胡乱讲述一下思维过程……
    首先,读懂题.
    然后,转化问题为构造一个长度为|T|+n的字符串,使其内含有T这个子序列.
    之后,想到一个简单的dp.由于是回文串,我们就增量构造半个回文串,设f(i,j,k)为构造到第i个位置,从前往后匹配到j,从后往前匹配到k,这样O(m*m*n)(没有观察到其转移的性质会再乘个26).
    再然后,发现不妙,在最后讨论奇偶.
    (我的思路到此为止……)
    接着,观察其转移的实质,发现其实dp的过程就是在一个有限状态自动机上行走,而有限状态自动机上的状态就是目前剩下的T,所以我们只不过是要求出从起点到某些点的路径条数(事先声明步数).
    到了现在,我们的目的只不过是统计到不同终点的路径条数,那么:第一步,点集相同的路径合并起来,点集不同,一定不同;第二步,对于点集相同的路径们,他们走自环的总数一定,分配不同,一定不同;第三步,对于点集相同且分配相同的路径们,他们每次走自环,走得不同,一定不同.接着观察,对于自环数一定的状态,是一样的,而且只有3种,并且他们的顺序也没有关系了(可以从组合数的角度看出),至此,对于每一种状态数量都相同且点集相同的路径,我们都可以合并,并且还可以算出合并之后的路径类在不计算内部影响下的贡献和.
    好了,现在该怎么处理上面最后合并出来的每一种路径的内部贡献呢?直接在原自动机上搞,肯定不行,每一种处理一次好一些,但是还是过不了,我们考虑建立一个大图,利用邻接矩阵的次幂的性质,我们就可以直接get到每两个点之间的距离,我们只要保证在这个大图上能get到所有我们想要的信息就可以了,所以现在我们把之前的自动机合并成一个大自动机就ok了.
    最后,把前两步的信息合并,就好了.
    好牛逼的有限状态自动机啊,感觉就像一个两端匹配的AC自动机,而且匹配的还是子序列.
    还有这个组合数学也是牛的一笔,甚至还利用组合意义合并路径.
    而且那个转大图建立邻接矩阵也是超66666666666666666
    (似乎还有组合数做法……看不懂……)
    (把dp状态搞到有限状态自动机上似乎是套路???)

    #pragma GCC optimize("O3")
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    const int P=10007,N=201;
    char s[N];
    int n,m,f[N][N][2],dp[N][N][N],R,G,B,S,a[N<<1][N<<1],temp[N<<1][N<<1],b[N<<1][N<<1];
    inline void DP(){
      register int i,j,l,r;
      dp[1][m][0]=1;
      for(i=m;i>0;--i)
        for(l=1,r=i;r<=m;++l,++r)
          for(j=0;j<=(m-i);++j){
            if(!dp[l][r][j])continue;
            if(s[l]==s[r]){
              if(l+1==r)
                (dp[0][0][j]+=dp[l][r][j])%=P;
              else if(l==r){
                (dp[0][0][j]+=dp[l][r][j])%=P;
                if(n&1)(f[j][(m-j+1)>>1][0]+=dp[l][r][j])%=P;
              }else
                (dp[l+1][r-1][j]+=dp[l][r][j])%=P;
            }else{
              (dp[l+1][r][j+1]+=dp[l][r][j])%=P;
              (dp[l][r-1][j+1]+=dp[l][r][j])%=P;
            }
          }
      for(i=0;i<=m;++i)
        (f[i][(m-i+1)>>1][1]+=((n&1)?26:1)*dp[0][0][i])%=P;
    }
    #define red(a) (a)
    #define green(a) (R+(a))
    #define blue(a) (R+G+(a))
    inline void Multi1(){
      memset(temp,0,sizeof(temp));
      register int i,j,k;
      for(i=1;i<=S;++i)
        for(j=1;j<=S;++j)
          if(a[i][j])
            for(k=1;k<=S;++k)
              if(b[j][k])
                (temp[i][k]+=a[i][j]*b[j][k])%=P;
      memcpy(b,temp,sizeof(b));
    }
    inline void Multi2(){
      memset(temp,0,sizeof(temp));
      register int i,j,k;
      for(i=1;i<=S;++i)
        for(j=1;j<=S;++j)
          if(a[i][j])
            for(k=1;k<=S;++k)
              if(a[j][k])
                (temp[i][k]+=a[i][j]*a[j][k])%=P;
      memcpy(a,temp,sizeof(a));
    }
    inline void MUL(){
      int i;
      R=m,G=(m+1)>>1,B=G,S=R+G+B;
      for(i=1;i<=R;++i){
        a[red(i)][red(i)]=24;
        if(i!=1)a[red(i)][red(i-1)]=1;
        else a[red(i)][green(1)]=1;
      }
      for(i=1;i<=G;++i){
        a[green(i)][green(i)]=25;
        a[green(i)][blue(i)]=1;
        if(i!=G)a[green(i)][green(i+1)]=1;
      }
      for(i=1;i<=B;++i)
        a[blue(i)][blue(i)]=26;
      for(i=1;i<=S;++i)b[i][i]=1;
      int mi=n>>1;
      while(mi){
        if(mi&1)Multi1();
        Multi2(),mi>>=1;
      }
    }
    inline void CAL(){
      int ans=0,i,j;
      for(i=0;i<=R;++i)
        for(j=1;j<=B;++j)
          if(f[i][j][1])
            (ans+=f[i][j][1]*b[i==0?green(1):red(i)][blue(j)])%=P;
      if(n&1)
        for(i=0;i<=R;++i)
          for(j=1;j<=G;++j)
            if(f[i][j][0])
              (ans+=f[i][j][0]*b[i==0?green(1):red(i)][green(j)])%=P;
      printf("%d
    ",ans);
    }
    int main(){
      scanf("%s%d",s+1,&n);
      m=strlen(s+1),n+=m;
      DP(),MUL(),CAL();
      return 0;
    }
  • 相关阅读:
    pg常用命令
    dmhs
    Redis集群
    Redis哨兵高可用架构
    Redis外网无法连接的问题
    Redis主从
    Redis持久化
    Redis安装
    Mysql执行计划详解
    Mysql安装配置
  • 原文地址:https://www.cnblogs.com/TSHugh/p/8470391.html
Copyright © 2020-2023  润新知