首页 > 代码库 > BZOJ1009: [HNOI2008]GT考试 矩阵快速幂+kmp+dp

BZOJ1009: [HNOI2008]GT考试 矩阵快速幂+kmp+dp

这个题你发现打暴力的话可以记忆化搜素加剪枝,那么意味着可以递推,我们搜的话就是1010^9我们就往下匹配遇到匹配成功就return,那么我们可以想一下什么决定了状态,我们考虑kmp的过程,对于我们目前匹配到的距离,下一次在匹配时不会用他之后的字符,那么只要我们知道匹配到的距离和已匹配长度就行了,那么我们考虑状态的转移,我们由于要像kmp那样匹配于是我们只要知道在匹配到k位时往下走一个数时匹配到哪,算出a[k][j](在k时到j的方案数),那么新的f[i][j]=∑f[i-1][k]*a[k][j],这里用到了对口遗传的思想,对于舍去的状态,我们不继承就是舍去了

至于钜乘(:??)去某位大佬博客

#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
int n,m,k;
char s[50];
int next[50];
int a[50][50],b[50][50],temp[50][50];
inline void kmp()
{
    a[m-1][m-0]+=1;
    a[m-0][m-0]+=9;
    for(int i=1,K=0;i<m;i++)
    {
      while(K&&s[K]!=s[i])
       K=next[K-1];
      if(s[K]==s[i])
       K++;
      next[i]=K;
      for(int j=0;j<=9;j++)
      {
        if(j+48==s[i])
        {
         a[m-i-1][m-i]+=1;
         continue;
        }
        int l=next[i-1];
        while(l&&s[l]!=j+48)
         l=next[l-1];
        if(s[l]==j+48)
         l++;
        a[m-l][m-i]+=1;
      }
    }
}
inline void Init()
{
    scanf("%d%d%d%s",&n,&m,&k,s);
    kmp();
}
inline void multi(int x[][50],int y[][50],int len)
{
    memset(temp,0,sizeof(temp));
    for(int i=1;i<=m;i++)
      for(int j=1;j<=len;j++)
        for(int l=1;l<=m;l++)
         temp[i][j]=(temp[i][j]+y[i][l]*x[l][j])%k;
    for(int i=1;i<=m;i++)
     for(int j=1;j<=len;j++)
      x[i][j]=temp[i][j];
}
inline void work()
{
    b[m][1]=1;
    while(n)
    {
      if(n&1)multi(b,a,1);
      n>>=1;
      multi(a,a,m);
    }
}
inline void print()
{
   int ans=0;
   for(int i=1;i<=m;i++)
    ans+=b[i][1];
   ans%=k;
   printf("%d",ans);
}
int main()
{
    Init();
    work();
    print();
    return 0;
}

 

BZOJ1009: [HNOI2008]GT考试 矩阵快速幂+kmp+dp