bzoj 1009: [HNOI2008]GT考试【kmp+dp+矩阵快速幂】

看n和k的范围长得就很像矩阵乘法了
设f[i][j]表示到第i个位置的后缀最长匹配目标串的j位。转移的话显然是枚举0~9,然后选择f[i+1]中能被他转移的加起来,需要用到next数组。然后构造矩阵的时候,在转移路径上++即可(注意代码里的f数组只是辅助构造矩阵的,和上文无关
在写挂了n次kmp之后我突然意识到一个问题:k<=20,我随便暴力个5、6次方的都没问题为啥要kmp……
结果还是用了kmp

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int M=30;
int n,m,mod,c[M],ne[M],f[M][M],an;
char s[M];
struct qwe
{
    int a[M][M];
    void init()
    {
        memset(a,0,sizeof(a));
    }
    qwe operator * (const qwe &b) const
    {
        qwe c;
        c.init();
        for(int k=0;k<m;k++)
            for(int i=0;i<m;i++)
                for(int j=0;j<m;j++)
                    c.a[i][j]=(c.a[i][j]+a[i][k]*b.a[k][j])%mod;
        return c;
    }
}a,sum;
void getne()
{
    // int i=0,j=-1;
    // ne[0]=-1;
    // while(i<m)
    // {
        // if(c[i]==c[j]||j==-1)
            // ne[++i]=++j;
        // else
            // j=ne[j];
    // }
    for(int i=1;i<m;i++)
    {
        int j=ne[i];
        while(j&&c[i+1]!=c[j+1]) 
            j=ne[j];
        ne[i+1]=c[i+1]==c[j+1]?j+1:0;
    }   
}
qwe ksm(int b)
{
    qwe r;
    r.init();
    for(int i=0;i<m;i++)
        r.a[i][i]=1;
    while(b)
    {
        if(b&1)
            r=r*a;
        a=a*a;
        b>>=1;
    }
    return r;
}
int main()
{
    scanf("%d%d%d%s",&n,&m,&mod,s+1);
    for(int i=1;i<=m;i++)
        c[i]=s[i]-'0';
    getne();
    for(int i=0;i<m;i++) 
        for(int j=0;j<=9;j++)
        {
            f[i][j]=c[i+1]==j?i+1:f[ne[i]][j];
            a.a[i][f[i][j]]++;
        }
    sum.a[0][0]=1;
    sum=sum*ksm(n);
    for(int i=0;i<m;i++)
        an=(an+sum.a[0][i])%mod;
    printf("%d\n",an);
    return 0;
}
posted @ 2018-03-19 20:00  lokiii  阅读(136)  评论(0编辑  收藏  举报