BZOJ 1009 GT考试(dp+矩阵快速幂)

本文介绍了一种用于生成GT考试准考证号的算法,该算法确保生成的准考证号不会包含考生认为不吉利的数字序列。通过使用动态规划和矩阵快速幂的方法,文章详细解释了如何高效地计算出符合要求的准考证号数量。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Description

阿申准备报名参加GTGT考试,准考证号为NN位数X1X2....Xn(0Xi9),他不希望准考证号上出现不吉利的数字。他的不吉利数学A1A2...Am(0Ai9)A1A2...Am(0≤Ai≤9)MM位,不出现是指X1X2...Xn中没有恰好一段等于A1A2...AmA1A2...Am.A1A1X1X1可以为00

Input

第一行输入N,M,K.接下来一行输入MM位的数(N109,M20,K1000)

Output

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

Sample Input

4 3 100

111

Sample Output

81

Solution

dp[i][j]表示前ii个字母已经确定,没有出现过不吉利数字,且末尾与不吉利数字最多匹配了j位的方案数,则答案即为j=0m1dp[n][j]∑j=0m−1dp[n][j],由于已经匹配了jj位,假设要在后面加上一个数字k(0k9),暴力求出此时这j+1j+1个数字与不吉利数字的最长匹配tt,只要tm,那么dp[i][j]dp[i][j]状态即可转移为dp[i+1][t]dp[i+1][t],以此求出dp[i]dp[i]dp[i+1]dp[i+1]的转移矩阵,矩阵快速幂加速转移即可

Code

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<vector>
#include<queue>
#include<map>
#include<set>
#include<ctime>
using namespace std;
typedef long long ll;
typedef int M[22][22];
int n,m,mod;
char s[22];
void Mul(M &A,M B)
{
    M C;
    for(int i=0;i<m;i++)
        for(int j=0;j<m;j++)
        {
            C[i][j]=0;
            for(int k=0;k<m;k++)C[i][j]=(C[i][j]+A[i][k]*B[k][j])%mod; 
        }
    for(int i=0;i<m;i++)
        for(int j=0;j<m;j++)
            A[i][j]=C[i][j];
}
void Pow(M &A,int k)
{
    M C;
    for(int i=0;i<m;i++)
        for(int j=0;j<m;j++)
            C[i][j]=(i==j);
    while(k)
    {
        if(k&1)Mul(C,A);
        Mul(A,A);
        k>>=1;
    }
    for(int i=0;i<m;i++)
        for(int j=0;j<m;j++)
            A[i][j]=C[i][j];
}
int main()
{
    scanf("%d%d%d%s",&n,&m,&mod,s+1);
    M A;
    for(int i=0;i<m;i++)
        for(int j=0;j<m;j++)
            A[i][j]=0;
    for(int j=0;j<m;j++)
        for(int k=0;k<=9;k++)
        {
            int i=j+1; 
            for(;i>=1;i--)
                if(s[i]-'0'==k)
                {
                    int flag=1;
                    for(int l=1;l<i;l++)
                        if(s[i-l]!=s[j-l+1])
                        {
                            flag=0;
                            break;
                        }
                    if(flag)break;
                }
            if(i!=m)A[i][j]++;
        }
    Pow(A,n);
    int ans=0;
    for(int i=0;i<m;i++)ans=(ans+A[i][0])%mod;
    printf("%d\n",ans);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值