【BZOJ 1009】GT考试

思路

动态规划

我们可以先考虑当 n 的范围较小时的做法,设f[i][j]表示现在处理到第 i 位,与不吉利数字已经成功匹配j位的方案数。
很显然的是,需要枚举 i 位出现的数字是哪一个,枚举完数字以后,如果与不幸运数字匹配成功,那么匹配位数+1,否则我们就需要知道失配之后的匹配位数,即需要用KMP算法预处理失配位。通过以上的处理可以得到 trans[i][j] ,表示已经匹配了不幸运数字的前 i 位,现在加入数字j后,原串的匹配个数。
状态转移方程: f[i+1][trans[j][k]]+=f[i][j]

矩阵优化

n 的范围是109,可用矩阵快速幂来加快转移。
我们可以通过初始矩阵和目的矩阵来构造目标矩阵。
首先我们要把要转移的信息集中在一起,构造一个方阵让其转移,没有的地方补 0 就可以。
所以初始矩阵和转移矩阵都是m×m的。
初始矩阵:

f[i][0]00f[i][1]00f[i][m]00

目标矩阵:
f[i+1][0]00f[i+1][1]00f[i+1][m]00

设转移矩阵为:
a11a21am1a12a22am2a1ma2mamm

需要明确的是,初始矩阵中第一行第 i 列的数转移的条件应转移矩阵的第i行的某个位置。
对于一个 f[i][a] ,可以转移到 f[i+1][b] ,那么我们就在转移矩阵的第 a 行,第b列的位置 +1
如果 a=m ,说明是不吉利的数字,不转移。

代码

#include <iostream>
#include <cstdio>
using namespace std;
int f[50], x[50], trans[50][15], mod, n, m, p, res;
char ch[50];
struct Matrix{
    int node[25][25];
    Matrix(){
        for(int i = 0; i <= 24; i ++) {
            for(int j = 0; j <= 24; j ++){
                node[i][j] = 0;
            }
        }
    }
    void init(){
        for(int i = 0; i <= m; i ++) node[i][i] = 1;
    }
    Matrix operator * (const Matrix &M){
        Matrix res;
        for(int i = 0; i < m; i ++)
            for(int j = 0; j < m; j ++)
                for(int k = 0; k < m; k ++)
                    res.node[i][j] = (res.node[i][j] + node[i][k]*M.node[k][j]) % mod;
        return res;
    }
} w, ans, Begin;
Matrix pow(Matrix x, int y){
    Matrix res;
    res.init();
    for( ; y; y >>= 1, x = x*x) if(y&1) res = res * x;
    return res; 
}
int main(){
    scanf("%d%d%d", &n, &m, &mod);
    scanf("%s", ch+1);
    for(int i = 1; i <= m; i ++) x[i] = ch[i]-'0';
    p = 0;
    for(int i = 2; i <= m; i ++){
        while(p && x[p+1] != x[i]) p = f[p];
        if(x[p+1] == x[i]) p ++;
        f[i] = p;
    }
    for(int i = 0; i < m; i ++){
        for(int j = 0; j <= 9; j ++){
            p = i;
            while(p && x[p+1] != j) p = f[p];
            if(x[p+1] == j) p ++;
            trans[i][j] = p;
        }
    }
    for(int i = 0; i < m; i ++){
        for(int j = 0; j <= 9; j ++){
            if(trans[i][j] != m) w.node[i][trans[i][j]] ++;
        }
    }
    Begin.node[0][0] = 1;
    ans = pow(w, n);
    ans = Begin * ans;
    for(int i = 0; i < m; i ++){
        res = (res + ans.node[0][i]) % mod;
    }
    printf("%d", res);
    return 0;
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值