[JZOJ1115]【HNOI2008】GT考试

Description

申准备报名参加GT考试,准考证号为n位数X1X2X3…Xn-1Xn(0<=Xi<=9),他不希望准考证号上出现不吉利的数字。
他的不吉利数字A1A2A3…Am-1Am(0<=Ai<=9)有m位,不出现是指X1X2X3…Xn-1Xn 中没有恰好一段等于A1A2A3…Am-1Am。A1和X1可以为0。
阿申想知道不出现不吉利数字的号码有多少种,输出模K取余的结果。
对100%的数据满足n<=10^9,m<=20,K<=1000;

Solution

明显可以考虑DP
f[i,j] 表示当前枚举到第 i 位,在X中匹配了 j 位,枚举当先位选什么。

我们发现,对于每一种j,它可以转移出的状态是一定的,或者说,转移来的状态与 i 无关。
转移过来的状态其实就是KMP匹配的上一位。

既然与i无关,且状态一定,那么显然可以用矩阵乘法解决。

Code

#include <cstdio>
#include <cmath>
#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdlib>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fod(i,a,b) for(int i=a;i>=b;i--)
#define M 22
using namespace std;
struct node
{
    int a[M][M];
}a,s;
int n,m,mo,num[M],p[M];
node ti(node a,node b)
{
    node c;
    memset(c.a,0,sizeof(c.a));
    fo(i,0,m)
        fo(j,0,m)
        {
            c.a[i][j]=0;
            fo(k,0,m) c.a[i][j]=(c.a[i][j]+a.a[i][k]*b.a[k][j])%mo;
        }
    return c;
}
node ksm(node k,int n)
{
    if(n==1) return k;
    node s=ksm(k,n/2);
    return(n%2)?ti(s,ti(s,k)):ti(s,s);
}
void kmp()
{
    int j=0;
    p[1]=0;
    fo(i,2,m)
    {
        while(num[j+1]!=num[i]&&j>0) j=p[j];
        if(num[j+1]==num[i]) j++;
        p[i]=j;
    }
}
int main()
{
    cin>>n>>m>>mo;
    scanf("\n");
    fo(i,1,m) 
    {
        char ch;
        scanf("%c",&ch);
        num[i]=ch-'0';
    }
    kmp();
    fo(i,0,m-1)
    {
        fo(j,0,9)
        {
            int q=i;
            while(num[q+1]!=j&&q>0) q=p[q];
            if(num[q+1]==j) q++;
            s.a[i][q]++;
        }
    }
    a.a[0][0]=1;
    a=ti(a,ksm(s,n));
    int ans=0;
    fo(i,0,m-1) ans=(ans+a.a[0][i])%mo;
    cout<<ans;

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值