1. 程式人生 > 實用技巧 >[HNOI2008] GT考試 - KMP,矩陣乘法,dp

[HNOI2008] GT考試 - KMP,矩陣乘法,dp

Description

准考證號為 \(N\) 位數 \(X_1,X_2…X_n(0\le X_i\le9)\),他不希望准考證號上出現不吉利的數字。他的不吉利數 \(A_1,A_2…A_m(0\le A_i\le 9)\)\(M\) 位,不出現是指 \(X_1,X_2…X_n\) 中沒有恰好一段等於 \(A_1,A_2…A_m\)\(A_1\)\(X_1\) 可以為 \(0\)

Solution

\(next[i]\) 表示 \(A[1..i]\) 的最長公共真前後綴,可以 KMP 預處理出

構造矩陣,當匹配到 \(i\) 位置 \(+c\) 可以轉移到 \(p\) 位置時,對 \(mat[p][i]++\)

這就是 DP 的轉移矩陣,快速冪處理即可

(實際上最後我們只需要統計冪矩陣的第一列的和)

#include <bits/stdc++.h>
using namespace std;

#define int long long
const int N = 25;

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

namespace kmp
{
    char *p=s;
    int n,m,fail[N];
    void main()
    {
        m=strlen(p+1);
        for(int i=2; i<=m; i++)
        {
            fail[i]=fail[i-1];
            while(p[fail[i]+1]-p[i] && fail[i]) fail[i]=fail[fail[i]];
            if(p[fail[i]+1]==p[i]) ++fail[i];
        }
        fail[0]=-1;
    }
}

struct matrix
{
    int n,a[N][N];

    matrix(int n=0):n(n)
    {
        memset(a,0,sizeof a);
    }

    int* operator [] (int i)
    {
        return a[i];
    }

    friend matrix operator * (matrix a,matrix b)
    {
        int n=a.n;
        matrix res(n);
        for(int i=1; i<=n; i++)
        {
            for(int j=1; j<=n; j++)
            {
                for(int k=1; k<=n; k++)
                {
                    res[i][k]=((res[i][k]+a[i][j]*b[j][k])%mod+mod)%mod;
                }
            }
        }
        return res;
    }

    matrix I()
    {
        matrix res(n);
        for(int i=1; i<=n; i++)
        {
            res[i][i]=1;
        }
        return res;
    }

    matrix operator ^ (int b)
    {
        if(b) return (((*this)*(*this))^(b/2))*(b&1?(*this):I());
        return I();
    }

    void print()
    {
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=n;j++) cout<<a[i][j]<<"\t";
            cout<<endl;
        }
    }
};

signed main()
{
    ios::sync_with_stdio(false);
    cin>>n>>m>>mod>>s+1;
    kmp::main();

    matrix mat(m);
    using kmp::fail;
    for(int i=0;i<m;i++)
    {
        for(char c='0';c<='9';c++)
        {
            int p=i;
            while(p && s[p+1]!=c) p=fail[p];
            if(s[p+1]==c) ++p;
            ++mat[p+1][i+1];
        }
    }
    //mat.print();
    mat=mat^n;
    //mat.print();
    int ans=0;
    for(int i=1;i<=m;i++) ans=(ans+mat[i][1])%mod;
    cout<<ans<<endl;
}