【bzoj1009】[HNOI2008]GT考试 矩阵+kmp+DP

Description

阿申准备报名参加GT考试,准考证号为N位数X1X2….Xn(0<=Xi<=9),他不希望准考证号上出现不吉利的数字。他的不吉利数学A1A2…Am(0<=Ai<=9)有M位,不出现是指X1X2…Xn中没有恰好一段等于A1A2…Am. A1和X1可以为0

Input

第一行输入N,M,K.接下来一行输入M位的数。 100%数据N<=10^9,M<=20,K<=1000 40%数据N<=1000 10%数据N<=6

Output

阿申想知道不出现不吉利数字的号码有多少种,输出模K取余的结果.

Sample Input

4 3 100 

111

Sample Output

81

HINT

Source

设dp[i][j]为第i个号码匹配到第j个不吉利数字的方案数。

显然前i个号码的后缀构成了前j个不吉利数字,而dp[i][j]向i+1转移相当于在后缀后面新加一个字符,可以联想到AC自动机,然而这个只有一个串所以kmp就足够了。

设a[k][j]为k位后面加一个字母转移到j的方案数,于是:

dp[i][j]=0<=k<=m1dp[i1][k]a[k][j]

我们发现k后面加一个字母转移到j可以用kmp实现。

这个式子是线性的,可以用矩阵优化。

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;

typedef long long LL;
const int SZ = 1000010;
const int INF = 1000000010;

int n,m,mod,nxt[233];

char s[233];

void getnxt(char s[])
{
    int l = strlen(s);
    nxt[0] = nxt[1] = 0;
    for(int i = 1;i < l;i ++)
    {
        int j = nxt[i];
        while(j && s[i] != s[j]) j = nxt[j];
        nxt[i + 1] = s[i] == s[j] ? j + 1 : 0;
    }
}

struct matrix{
    int n,m,num[30][30];
    matrix(int a,int b) : n(a),m(b) {memset(num,0,sizeof(num));}
};

matrix operator *(const matrix &a,const matrix &b)
{
    matrix ans(a.n,b.m);
    for(int i = 0;i < ans.n;i ++)
        for(int j = 0;j < ans.m;j ++)
            for(int k = 0;k < a.m;k ++)
                ans.num[i][j] = (ans.num[i][j] + (LL)a.num[i][k] * b.num[k][j] % mod) % mod;
    return ans;
}

matrix ksm(matrix a,int b)
{
    matrix ans(a.n,a.m);
    for(int i = 0;i < ans.n;i ++)
        ans.num[i][i] = 1;
    while(b)
    {
        if(b & 1) ans = ans * a;
        a = a * a;
        b >>= 1;
    }
    return ans;
}

int main()
{
    scanf("%d%d%d%s",&n,&m,&mod,s);

    getnxt(s);

    matrix f(m,m);

    for(int i = 0;i < m;i ++)
    {
        for(char j = '0';j <= '9';j ++)
        {
            int k = i;
            while(k && s[k] != j) k = nxt[k];
            if(s[k] == j) k ++;
            if(k != m) f.num[i][k] ++;
        }
    }
    matrix fn = ksm(f,n),a(1,m);
    a.num[0][0] = 1;
    a = a * fn;
    int ans = 0;
    for(int i = 0;i < m;i ++)
        ans = (ans + a.num[0][i]) % mod;
    printf("%d\n",ans);
    return 0;
}

你可能感兴趣的:(【bzoj1009】[HNOI2008]GT考试 矩阵+kmp+DP)