BZOJ 1009 KMP+矩阵快速幂优化DP

1009: [HNOI2008]GT考试

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位的数。 N<=10^9,M<=20,K<=1000

Output

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

Sample Input

4 3 100
111

Sample Output

81

【解题报告】

首先我们令 dp(i,j) 表示当前我们枚举到第i位的时候我们枚举的后缀最长和不吉利的字符串的前缀已经匹配了j个的时候的方案数量,那么我们的答案就是
i=0m1dp(n,i)
那么我们可以发现我们令 p(i,j) 表示从不吉利的字符串第i个字符后添加一个数字就可以从i个匹配变成j个匹配的可以填写的数字的个数那么我们显然有
dp(i,j)=k=0m1(dp(i1,k)×p(k,j))
那么我们的p显然可以进行预处理,那么我们发现p是可以表示成一个矩阵的同时我们发现dp也可以表示成一个矩阵那么我们显然有
DPi=DPi1×P
那么我们就有
DPn=DPn1×P=DP0×Pn
使用矩阵加速进行优化

代码如下:

/**************************************************************
    Problem: 1009
    User: onepointo
    Language: C++
    Result: Accepted
    Time:56 ms
    Memory:1952 kb
****************************************************************/

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define N 50

int n,m;
int MOD,Fail[N];
char s[N];

struct Matrix
{
    int Ma[N][N],n,m;
    void Clear(int u,int un,int um)
    {
        n=un,m=um;
        memset(Ma,0,sizeof(Ma));
        if(u) for(int i=0;i<un;++i) Ma[i][i]=1;
    }
    Matrix operator * (const Matrix &ma) 
    {
        Matrix ret;
        ret.Clear(0,n,m);
        for(int i=0;i<n;++i)
        for(int j=0;j<ma.m;++j)
        for(int k=0;k<ma.n;k++) 
        {
            ret.Ma[i][j]+=Ma[i][k]*ma.Ma[k][j];
            ret.Ma[i][j]%=MOD;
        }
        return ret;
    }
};
Matrix Mpow(Matrix m,int p)
{
    Matrix ret;
    if(p==0)
    {
        ret.Clear(1,2,2);
        return ret;
    }
    else if(p==1) return m;
    ret=Mpow(m,p/2);
    if(p%2==0) return ret*ret;
    return (ret*ret)*m;
}
int main()
{
    scanf("%d%d%d",&n,&m,&MOD);
    scanf("%s",s+1);
    Matrix mtr;
    mtr.Clear(0,m,m);
    mtr.Ma[0][0]=9;mtr.Ma[0][1]=1;
    for(int i=1;i<m;++i)
    {
        int us=Fail[i];
        while(us)
        {
            if(s[us+1]==s[i+1]) break;
            us=Fail[us];
        }
        if(s[us+1]==s[i+1]) Fail[i+1]=us+1;
        else Fail[i+1]=0;
        for(int j=0;j<=9;++j)
        {
            int u=i;
            while(u&&s[u+1]!=j+'0') u=Fail[u];
            if(s[u+1] == j+'0') mtr.Ma[i][u+1]=(mtr.Ma[i][u+1]+1)%MOD;
            else mtr.Ma[i][0]=(mtr.Ma[i][0]+1) % MOD;
        }
    }
    Matrix ans=Mpow(mtr,n);
    int cnt=0;
    for(int i=0;i<m;++i)
        (cnt+=ans.Ma[0][i])%MOD;
    printf("%d\n",(cnt+MOD)%MOD);
    return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值