洛谷3193 [HNOI2008]GT考试

Luogu

BZOJ

分析

考虑一个 naive 的 DP。设 $f_{i,j}$ 表示前 $i$ 位匹配到不吉利数的第 $j$ 位的方案数。

设 $g_{i,j}$ 表示在匹配到第 $i$ 位时加一个字符会匹配到第 $j$ 位的方案数,那么

$$ f_{i,j}=\sum_{k=0}^{m-1}f_{i-1,k}\times g_{k,j} $$

考虑如何求出 $g$。枚举每一位以及后面加的字符,然后爆跳 nxt 即可找到加字符后转移到的位置。

然后注意到上面的 DP 式像矩阵乘法的形式,直接矩阵快速幂即可。

代码

// ===================================
//   author: M_sea
//   website: http://m-sea-blog.com/
// =================================== 
#include <algorithm>
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <cmath>
#define re register
using namespace std;

inline int read() {
    int X=0,w=1; char c=getchar();
    while (c<'0'||c>'9') { if (c=='-') w=-1; c=getchar(); }
    while (c>='0'&&c<='9') X=X*10+c-'0',c=getchar();
    return X*w;
}

const int N=20+10;

int n,m,mod; char s[N];

struct Matrix {
    int a[N][N];
    Matrix() { memset(a,0,sizeof(a)); }
    int* operator [](int i) { return a[i]; }
} G;
Matrix operator *(Matrix a,Matrix b) {
    Matrix c;
    for (re int i=0;i<m;++i)
        for (re int j=0;j<m;++j) {
            for (re int k=0;k<m;++k) c[i][j]+=a[i][k]*b[k][j];
            c[i][j]%=mod;
        }
    return c;
}
inline Matrix qpow(Matrix a,int b) {
    Matrix c; for (re int i=0;i<m;++i) c[i][i]=1;
    for (;b;b>>=1,a=a*a) if (b&1) c=c*a;
    return c;
}

int nxt[N];
inline void GetNext() {
    for (re int i=2,j=0;i<=m;++i) {
        while (j&&s[j+1]!=s[i]) j=nxt[j];
        if (s[j+1]==s[i]) ++j;
        nxt[i]=j;
    }
}
inline void KMP() {
    for (re int i=0;i<m;++i)
        for (re char j='0';j<='9';++j) {
            int k=i;
            while (k&&s[k+1]!=j) k=nxt[k];
            if (s[k+1]==j) ++k;
            ++G[i][k];
        }
}

int main() {
    n=read(),m=read(),mod=read(); scanf("%s",s+1);
    GetNext(); KMP(); G=qpow(G,n);
    int ans=0;
    for (re int i=0;i<m;++i) ans=(ans+G[0][i])%mod;
    printf("%d\n",ans);
    return 0;
}
最后修改:2019 年 10 月 08 日 07 : 44 PM

发表评论