「Bzoj 1009」「HNOI2008」GT考试 (匹配状态DP+KMP+矩阵快速幂)

Bzoj 1009
题意:给定$m$长值域$[0,9]$数列$A_i$,要求构造$n$长值域$[0,9]$数列$X_i$使得$A_i$不是$X_i$的子串,即$X_i$中不出现$A_i$,求方案数。$n \leq 10^9, m \leq 20$

类似CF 1096DCF 1015F以及AC自动机的相关方法,我们这里可以定义$dp(i,j)$为构造串$X_i$前$i$个字符与$A_i$匹配了$j$位的方案数。最后答案即为$\sum_{i=0}^{m-1} dp(n,i)$,即不包含完全匹配的串的方案
转移方程为(刷表):
$$
dp(i+1,x)+=dp(i,j)
$$
$x$为在当前后面放一个$[0,9]$之间的数后匹配的影响,特别的,如果匹配了$m$位,以后转移都只能从$m$转移而来。这个$x$可用 $KMP$ 预处理。
这里$n \leq 10^9$ 显然矩阵优化,构造矩阵即可。
注意$j=0$处转移矩阵:$dp(i,0)$一定可以以某个$[0,9]$之间的数转移到$dp(i+1, 1)$,所以在矩阵中设$(0,1)=1$;
$dp(i,0)$一定可以有$9$种情况转移到$dp(i+1, 0)$,即仍然不匹配。所以在矩阵中设$(0,0)=1$

发现一个新的 $Debug$ 方法:过不去样例,可以测测自己写的小数据,这个数据可能非常小,如果程序都过不去的话,那么肯定这里都有问题。就能各个击破,发现 $Bug$。

知识点:
1、过不了样例可以测测自己出的小数据

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<vector>
#include<queue>
#include<cmath>
#define ms(i, j) memset(i, j, sizeof i)
#define LL long long
#define db double
#define fir first
#define sec second
#define mp make_pair
using namespace std;
namespace flyinthesky {
LL n, m, p, a[20 + 5], f[20 + 5], whw[20 + 5][10 + 5];
// n 位数. m->a[maxM], f[maxM], whw[maxM][0~9]
struct matrix {
LL x, y, a[20 + 5][20 + 5];
matrix(LL _x, LL _y) {x = _x, y = _y, ms(a, 0);}
};
matrix mul(matrix a, matrix b) {
matrix ret(a.x, b.y);
for (LL i = 0; i <= a.x; ++i) {
for (LL j = 0; j <= b.y; ++j) {
for (LL k = 0; k <= a.y; ++k) {
ret.a[i][j] = (ret.a[i][j] + (a.a[i][k] * b.a[k][j]) % p) % p;
}
}
}
return ret;
}
matrix ksm(matrix a, LL b) {
matrix bs = a, ans(a.x, a.y);
LL fl = 0;
while (b) {
if (b & 1) {
if (!fl) fl = 1, ans = bs; else ans = mul(ans, bs);
}
bs = mul(bs, bs);
b >>= 1;
}
return ans;
}
void clean() {
ms(whw, 0);
}
int solve() {
clean();
scanf("%lld%lld%lld", &n, &m, &p);
for (LL i = 1; i <= m; ++i) scanf("%1lld", &a[i]);
f[1] = f[2] = 1;
for (LL i = 2; i <= m; ++i) {
LL j = f[i];
while (j > 1 && a[i] != a[j]) j = f[j];
f[i + 1] = (a[i] == a[j] ? j + 1 : 1);
}
for (LL i = 1; i <= m; ++i) {
for (LL x = 0; x <= 9; ++x) {
LL j = i;
while (j > 1 && a[j] != x) j = f[j];
whw[i][x] = (a[j] == x ? j : 0);
}
}
matrix gg(m, m), hh(1, m);
gg.a[0][0] = 9;
gg.a[0][1] = 1;
for (LL i = 1; i < m; ++i) {
for (LL x = 0; x <= 9; ++x) {
++gg.a[i][whw[i + 1][x]];
}
}
++gg.a[m][m];
// for (LL i = 0; i <= m; ++i, puts(""))for (LL j = 0; j <= m; ++j) printf("%d ", gg.a[i][j]);
gg = ksm(gg, n);
// for (LL i = 0; i <= m; ++i, puts(""))for (LL j = 0; j <= m; ++j) printf("%d ", gg.a[i][j]);
hh.a[0][0] = 1;
hh = mul(hh, gg);
// for (LL i = 0; i <= m; ++i, puts(""))for (LL j = 0; j <= m; ++j) printf("%d ", hh.a[i][j]);
LL ans = 0;
for (LL i = 0; i < m; ++i) ans = (ans + hh.a[0][i]) % p;
cout << ans;
return 0;
}
}
int main() {
flyinthesky::solve();
return 0;
}
/*
2 2 10000000
11
1 1 10000000
1
*/
------ 本文结束 ------