BZOJ 1009: [HNOI2008]GT考试

妈耶之前因为不熟KMP一直觉得这题好难,现在发现当年真是naive

首先我们容易设出一个DP,\(f_{i,j}\)表示准考证上前\(i\)位的长度为\(j\)的后缀与不吉利的数字的长度为\(j\)的前缀匹配的方案数

那么显然\(ans=\sum_{i=0}^{m-1} f_{n,i}\),考虑\(f\)如何转移

假设现在做到\(i\)的位置,\(f_{i-1}\)显然是已知的,那么就考虑多加入一个数字会带来什么影响

由于这里DP的特性,我们设一个\(g_{i,j}\)表示在不吉利的数字串上匹配时,加入一个数字,能加入多少种,使得长度为\(i\)的匹配变成长度为\(j\)的匹配

画画图就会发现原来和准考证号匹配的是前缀,而这里加入的是后缀,因此就是个最大前缀后缀匹配的问题,这就是一个裸KMP啊

然后我们轻易地求出了\(g\),然后发现这个转移就是个矩乘的形式,因此直接做就好了

#include<cstdio>
#include<cstring>
#define RI register int
#define Ms(f,x) memset(f,x,sizeof(f))
using namespace std;
const int R=25;
struct Matrix
{
    int n,m,a[R][R];
    Matrix(int N=0,int M=0) { n=N; m=M; Ms(a,0); }
    inline void Cir_init(void)
    {
        for (RI i=0;i<n;++i) a[i][i]=1;
    }
}; int n,m,mod,next[R],ans; char s[R];
inline void KMP(void)
{
    int len=0; for (RI i=2;i<=m;++i)
    {
        while (len&&s[len+1]!=s[i]) len=next[len];
        if (s[len+1]==s[i]) ++len; next[i]=len;
    }
}
inline void inc(int &x,int y)
{
    if ((x+=y)>=mod) x-=mod;
}
inline Matrix operator *(Matrix A,Matrix B)
{
    Matrix C(A.n,B.m); for (RI i=0;i<A.n;++i)
    for (RI j=0;j<B.m;++j) for (RI k=0;k<A.m;++k)
    inc(C.a[i][j],1LL*A.a[i][k]*B.a[k][j]%mod); return C;
}
inline Matrix operator ^(Matrix A,int p)
{
    Matrix T(m,m); T.Cir_init();
    for (;p;p>>=1,A=A*A) if (p&1) T=T*A; return T;
}
int main()
{
    RI i,j; scanf("%d%d%d%s",&n,&m,&mod,s+1); Matrix A(m,m);
    for (KMP(),i=0;i<m;++i) for (j=0;j<10;++j)
    {
        int len=i; while (len&&s[len+1]-'0'!=j) len=next[len];
        if (s[len+1]-'0'==j) ++len; inc(A.a[i][len],1);
    }
    A=A^n; for (i=0;i<m;++i) inc(ans,A.a[0][i]);
    return printf("%d",ans),0;
}
posted @ 2020-01-31 15:16  空気力学の詩  阅读(101)  评论(0编辑  收藏  举报