[BZOJ1009][HNOI2008]GT考试

[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位的数。 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

Solution:
利用动态规划计数。我们设计状态为f[i][j],表示目前位于准考证号的第i位,匹配到不吉利数字的第j位,这样转移方程显然,我们只需要利用kmp即可。
显然f[i][] -> f[i+1][]的转移对于任意一个i都是一样的,这就启发我们利用矩阵乘法加速。

Code :

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<string>
#include<vector>
#include<cstring>
#define rep(i,n) for(int i=0;i<n;i++)
#define pb push_back
using namespace std;
const int inf=~0U>>1,maxm=20;
int n,m,mod,A[maxm],next[maxm+1];
struct Mat
{
    int M[maxm][maxm];
    Mat(){memset(M,0,sizeof(M));}
    void operator=(const Mat&o)
    {memcpy(M,o.M,sizeof(M));}
    int& operator()(int i,int j){return M[i][j];}
    Mat operator*(Mat&o)
    {
        Mat T;
        rep(i,m)rep(j,m)rep(k,m)T(i,j)+=M[i][k]*o(k,j),T(i,j)%=mod;
        return T;
    }
}orig;
Mat Power(int n)
{
    if(n==1) return orig;
    Mat tmp=Power(n/2);
    tmp=tmp*tmp;
    if(n&1) tmp=tmp*orig;
    return tmp;
}
int main()
{
    cin>>n>>m>>mod;char x;
    rep(i,m) cin>>x,A[i+1]=x-'0';
    next[1]=0;int t=0;
    for(int i=2;i<=m;i++)
    {
        while(t>0&&A[t+1]!=A[i]) t=next[t];
        if(A[t+1]==A[i])++t;
        next[i]=t;
    }
    for(int i=0;i<m;++i)
    {
        rep(j,10)
        {
            int t=i;
            while(t>0&&A[t+1]!=j)t=next[t];
            if(A[t+1]==j)++t;
            orig(t,i)++;
        }
    }
    Mat ans=Power(n);int Ans=0;
    rep(i,m) Ans+=ans(i,0),Ans%=mod;
    cout<<Ans<<endl;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值