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

52 篇文章 0 订阅
50 篇文章 0 订阅

Description

阿申准备报名参加 GT G T 考试,准考证号为 N N 位数X1X2....Xn(0Xi9),他不希望准考证号上出现不吉利的数字。他的不吉利数学 A1A2...Am(0Ai9) A 1 A 2... A m ( 0 ≤ A i ≤ 9 ) M M 位,不出现是指X1X2...Xn中没有恰好一段等于 A1A2...Am A 1 A 2... A m . A1 A 1 X1 X 1 可以为 0 0

Input

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

Output

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

Sample Input

4 3 100

111

Sample Output

81

Solution

dp[i][j]表示前 i i 个字母已经确定,没有出现过不吉利数字,且末尾与不吉利数字最多匹配了j位的方案数,则答案即为 j=0m1dp[n][j] ∑ j = 0 m − 1 d p [ n ] [ j ] ,由于已经匹配了 j j 位,假设要在后面加上一个数字k(0k9),暴力求出此时这 j+1 j + 1 个数字与不吉利数字的最长匹配 t t ,只要tm,那么 dp[i][j] d p [ i ] [ j ] 状态即可转移为 dp[i+1][t] d p [ i + 1 ] [ t ] ,以此求出 dp[i] d p [ i ] dp[i+1] d p [ 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;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值