【题解】HNOI2008GT考试
这题好难啊……完全不懂矩阵加速递推的我TAT
这道题目要求我们求出不含不吉利数字的字符串总数,那么我们有dp方程 : dp[i][j](长度为 i 的字符串,最长与不吉利数字前缀相同的后缀长度为 j 的方案数)。 dp[i][j] = Σdp[i - 1][k] * a[k][j] (a 数组表示从 k 状态转移到 j 状态的方案数)。a 数组我们可以通过 kmp 对不吉利数字的每一个前缀后面加上‘0’~‘9’转移匹配得到(匹配成功表示成功转移状态,a[k][j]++;否则表示此时没有重合的后缀,a[k][0]++)。
此时这道题目我们已经拥有了一个相对优的解法了,但是还不够。注意到上面的式子,我们对于dp数组与a数组分别建立矩阵,dp矩阵是一个列矩阵,一列代表1~k的状态,a矩阵第 j 行上每个数分别表示a[j][k]。所以得到的答案dp[i][j]即为dp矩阵与a矩阵第 j 行的乘积。矩阵快速幂优化即可。
#include <bits/stdc++.h> using namespace std; #define maxn 100000 int n, m, Mod, k, ans; char s[maxn], nxt[maxn]; int read() { int x = 0, k = 1; char c; c = getchar(); while(c < '0' || c > '9') { if(c == '-') k = -1; c = getchar(); } while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar(); return x * k; } struct Matrix { int num[52][52]; void init() { memset(num, 0, sizeof(num)); } Matrix operator*(const Matrix &x) { Matrix tem; tem.init(); for(int i = 0; i < m; i ++) for(int j = 0; j < m; j ++) for(int k = 0; k < m; k ++) { tem.num[i][j] += (num[i][k] * x.num[k][j]) % Mod; tem.num[i][j] %= Mod; } return tem; } }T, S; void KMP() { int j = 0; for(int i = 2; i <= m; i ++) { while(j && s[j + 1] != s[i]) j = nxt[j]; if(s[j + 1] == s[i]) j ++; nxt[i] = j; } j = 0; for(int i = 0; i < m; i ++) for(int k = 0; k <= 9; k ++) { j = i; while(j && s[j + 1] != (char) k + '0') j = nxt[j]; if(s[j + 1] == (char) k + '0') T.num[i][j + 1] ++; else T.num[i][0] ++; } } void Qpow() { for(int i = 0; i < m; i ++) S.num[i][i] = 1; while(n) { if(n & 1) S = S * T; T = T * T; n >>= 1; } } int main() { n = read(), m = read(), k = read(); Mod = k; scanf("%s", s + 1); KMP(); Qpow(); for(int i = 0; i < m; i ++) ans = (ans + S.num[0][i]) % Mod; printf("%d\n", ans); return 0; }