阿申准备报名参加GT考试,准考证号为 N N 位数 ,他不希望准考证号上出现不吉利的数字。他的不吉利数字 M M 位,不出现是指 中没有恰好一段等于 , 特别地 A1 A_1 x1 x_1 可以为 0 0

题解

首先这题数据范围非常可爱, 1N109,1m20 1 \leq N \leq 10 ^ 9, 1 \leq m \leq 20 ,这个数据范围会让人不自觉的想起矩乘。

f(i,j) f(i, j) 表示已经确定了准考证号的前 i i 位,在这 i i 位的末尾出现了不吉利数字的 j j 个。

枚举 09 0 \sim 9 转移,考虑在第 i+1 i + 1 位上添上某一个数字,可能转移到

用语言来描述的话,就是新添加的这个数字可能会让最后的不吉利数字延长,或者让末位的某几位变为原来出现的连续不吉利数字的前缀。

其实这个东西随便暴力啦,毕竟 m m 只有 20 20 。。

剩下的事情就是构造转移矩阵了,由于每次转移只和 j j 有关,所以可以轻松预处理转移矩阵。

代码

#include <cstdio>
#include <cstring>
#include <climits>
#include <algorithm>
const int MAX_N = 1e9;
const int MAX_M = 20;
struct Matrix {
int a[MAX_M + 5][MAX_M + 5];
int n, m;
Matrix(int n, int m, bool unit = false) {
this->n = n;
this->m = m;
memset(a, 0, sizeof(a));
if (unit) {
for (int i = 0; i <= n; i++) a[i][i] = 1;
}
}
};
int n, m, mod;
char s[MAX_M + 1];
int next[MAX_M + 1];
Matrix operator*(const Matrix &a, const Matrix &b) {
Matrix res(a.n, b.m, false);
for (int i = 0; i < a.n; i++) {
for (int j = 0; j < b.m; j++) {
for (int k = 0; k < a.m; k++) {
(res.a[i][j] += a.a[i][k] * b.a[k][j]) %= mod;
}
}
}
return res;
}
Matrix pow(Matrix a, int n) {
Matrix res(a.n, a.n, true);
for (; n; n >>= 1, a = a * a) if (n & 1) res = res * a;
return res;
}
inline void getNext() {
int j = 0;
for (int i = 2; i <= m; i++) {
while (j > 0 && s[j + 1] != s[i]) j = next[j];
if (s[j + 1] == s[i]) j++;
next[i] = j;
}
}
int main() {
scanf("%d %d %d", &n, &m, &mod);
scanf("%s", s + 1);
getNext();
Matrix shift(m, m, false);
Matrix origin(m, 1, false);
for (int i = 0; i < m; i++) {
for (int j = 0; j <= 9; j++) {
int k = i;
while (k > 0 && s[k + 1] - '0' != j) k = next[k];
if (s[k + 1] - '0' == j) k++;
(shift.a[k][i] += 1) %= mod;
}
}
origin.a[0][0] = 1;
Matrix res = pow(shift, n) * origin;
int ans = 0;
for (int i = 0; i < m; i++) (ans += res.a[i][0]) %= mod;
printf("%d\n", ans);
return 0;
}