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

来源:互联网 发布:发那科机器人示教编程 编辑:程序博客网 时间:2024/05/29 02:51

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;}
0 0
原创粉丝点击