二次联通门 : BZOJ 1009: [HNOI2008]GT考试
/* BZOJ 1009: [HNOI2008]GT考试 Kmp预处理 + 矩阵优化dp dp[i][j] 表示第i个号码匹配到了第j个不吉利的数字的方案数 S[x][y]为dp[i - 1][x] 转移到 dp[i][y]的方案数 S[x][y]就是长度为x的一个前缀,加上一个数字后, 后缀的最长长度为y的前缀匹配 能加的数字有多少种 后转为矩阵就好了 最烦的就是字符串0,1号位的选择问题了。。。 */ #include <cstring> #include <cstdio> #define Max 100 void read (int &now) { now = 0; register char word = getchar (); while (word < '0' || word > '9') word = getchar (); while (word >= '0' && word <= '9') { now = now * 10 + word - '0'; word = getchar (); } } int Mod; int N, M; char line[Max]; struct Martix_Data { int data[Max][Max]; Martix_Data operator * (const Martix_Data &now) const { Martix_Data res; for (register int i = 0; i < M; i ++) for (register int j = 0; j < M; j ++) { res.data[i][j] = 0; for (register int k = 0; k < M; k ++) res.data[i][j] = (res.data[i][j] + this->data[i][k] * now.data[k][j]) % Mod; } return res; } }; Martix_Data S; Martix_Data Answer; Martix_Data operator ^ (Martix_Data now, int P) { Martix_Data res = Answer; for (; P; now = now * now, P >>= 1) if (P & 1) res = now * res; return res; } int next[Max]; void Kmp_Prepare (char *__line) { int pos = 0; for (int i = 2; i <= M; i ++) { for (; pos && __line[pos + 1] != __line[i]; ) pos = next[pos]; if (__line[pos + 1] == __line[i]) pos ++; next[i] = pos; } for (int i = 0, pos; i < M; i ++) for (char j = '0'; j <= '9'; j ++) { for (pos = i; pos && __line[pos + 1] != j; ) pos = next[pos]; if (j == __line[pos + 1]) S.data[i][pos + 1] ++; else S.data[i][0] ++; } } int main (int argc, char *argv[]) { read (N); read (M); read (Mod); scanf ("%s", line + 1); Kmp_Prepare (line); for (int i = 0; i < M; i ++) Answer.data[i][i] = 1; Answer = S ^ N; int Total = 0; for (int i = 0; i < M; i ++) Total = (Total + Answer.data[0][i]) % Mod; printf ("%d", Total); return 0; }