[bzoj 1009] [HNOI2008]GT考试:DP,单串AC自动机,矩阵快速幂
2016-11-01 13:35
375 查看
题意:给一个长为M(M<=20)的十进制数字串,求有多少个长为N(N<=10^9)的十进制数字串不包含它,结果对K(K<=10^3)取模。
在黄学长博客的KMP分类中找到这题。并没产生什么好思路……准确地说是没深入思考,应该批评。如果把
把这个暴力放到AC自动机上。这里只有一串。
另一种理解方式。每一个合法文本串对应Trie图上从起点开始的一条路径。求出Trie图的邻接矩阵。对邻接矩阵做矩阵乘法,即得任意两点间恰好经过N条边的路径数。
解法来源于[bzoj 1030] [JSOI2007]文本生成器。
用矩阵乘法优化DP的关键在于构造转移矩阵A。如果想让转移矩阵乘到右边,那么
我的矩阵快速幂写的不是很优雅……几个数组复制来复制去的……黄学长用迭代写的,很精简。
一开始我真的是用KMP的失配指针转移的……没仔细想。失配不一定只跳一次嘛,这样怎么能让长度为N的字符串和经过N条边的路径对应呢?其实AC自动机也有多种写法,这里要用补完版本。
放两份代码。第二份是纯暴力,可以用于检验小数据。
在黄学长博客的KMP分类中找到这题。并没产生什么好思路……准确地说是没深入思考,应该批评。如果把
f[i][S]——前i个字符,以S为(M-1)后缀——作为状态,那么来个20维的DP是能想到的……枚举第(i+1)个字符,只要不形成给定的模板串就可以转移。
把这个暴力放到AC自动机上。这里只有一串。
f[i][j]表示文本串的前i个字符匹配到Trie图前j个结点的方案数。如果有一条边(j, k),那么
f[i+1][k] += f[i][j]。矩阵快速幂优化。
另一种理解方式。每一个合法文本串对应Trie图上从起点开始的一条路径。求出Trie图的邻接矩阵。对邻接矩阵做矩阵乘法,即得任意两点间恰好经过N条边的路径数。
解法来源于[bzoj 1030] [JSOI2007]文本生成器。
用矩阵乘法优化DP的关键在于构造转移矩阵A。如果想让转移矩阵乘到右边,那么
A[i][j]是i对j的贡献。如果想让转移矩阵乘到左边,那么
A[i][j]是i从j得到的贡献。
我的矩阵快速幂写的不是很优雅……几个数组复制来复制去的……黄学长用迭代写的,很精简。
一开始我真的是用KMP的失配指针转移的……没仔细想。失配不一定只跳一次嘛,这样怎么能让长度为N的字符串和经过N条边的路径对应呢?其实AC自动机也有多种写法,这里要用补完版本。
放两份代码。第二份是纯暴力,可以用于检验小数据。
#include <cstdio> #include <cstring> using namespace std; int N, M, K; const int MAX_M = 20, sz = sizeof(int)*MAX_M*MAX_M, SS = 10; char a[MAX_M+1]; int A[MAX_M][MAX_M], next[MAX_M][10], fail[MAX_M]; void build() { A[0][0] = SS-1; if (M == 1) return; A[0][1] = 1; next[0][a[0]] = 1; for (int i = 1; i < M; ++i) { if (i > 1) fail[i] = next[fail[i-1]][a[i-1]]; for (int j = 0; j < SS; ++j) { if (i == M-1 && j == a[i]) continue; next[i][j] = j == a[i] ? i+1 : next[fail[i]][j]; if (++A[i][next[i][j]] == K) A[i][next[i][j]] = 0; } } } void matrix_mul(int C[MAX_M][MAX_M], int A[MAX_M][MAX_M], int B[MAX_M][MAX_M]) { memset(C, 0, sz); for (int i = 0; i < M; ++i) for (int j = 0; j < M; ++j) for (int k = 0; k < M; ++k) (C[i][j] += A[i][k] * B[k][j]) %= K; } void matrix_exp(int A[MAX_M][MAX_M], int n) { if (n == 0) { memset(A, 0, sz); for (int i = 0; i < M; ++i) A[i][i] = 1; return; } if (n == 1) return; int B[MAX_M][MAX_M], C[MAX_M][MAX_M]; memcpy(B, A, sz); // B = A matrix_exp(A, n/2); matrix_mul(C, A, A); if (n & 1) matrix_mul(A, C, B); else memcpy(A, C, sz); // A = C } int main() { scanf("%d %d %d %s", &N, &M, &K, a); for (int i = 0; i < M; ++i) a[i] -= '0'; build(); matrix_exp(A, N); int ans = 0; for (int j = 0; j < M; ++j) ans = (ans + A[0][j]) % K; printf("%d\n", ans); return 0; }
#include <cstdio> #include <cstring> using namespace std; const int MAX_N = 10, MAX_M = 10, SS = 10; int N, M, K; char a[MAX_M], b[MAX_N]; int search(int k) { int ans = 0; if (k == N) { bool ok = false; for (int i = 0; i < N; ++i) if (i+M <= N && !strncmp(a, b+i, M)) { ok = true; break; } return !ok; } for (int i = 0; i < SS; ++i) { b[k] = i+'0'; (ans += search(k+1)) %= K; } return ans; } int main() { scanf("%d %d %d %s", &N, &M, &K, a); printf("%d\n", search(0)); return 0; }
相关文章推荐
- [KMP DP 矩阵快速幂加速] BZOJ 1009 [HNOI2008]GT考试
- [BZOJ 1009] [HNOI2008] GT考试 【AC自动机 + 矩阵乘法优化DP】
- BZOJ1009: [HNOI2008]GT考试 矩阵快速幂+kmp+dp
- 【BZOJ1009】GT考试(HNOI2008)-DP矩阵优化+KMP
- BZOJ 1009 [HNOI2008]GT考试 (KMP + 矩阵快速幂)
- 【bzoj1009】[HNOI2008]GT考试 矩阵+kmp+DP
- BZOJ 1009 [HNOI2008]GT考试 矩阵乘法+DP
- [BZOJ 1009][HNOI2008]GT考试(KMP+线性齐次递推的矩阵加速?+DP)
- BZOJ 1009: [HNOI2008]GT考试( dp + 矩阵快速幂 + kmp )
- _bzoj1009 [HNOI2008]GT考试【矩阵加速dp】
- 1009: [HNOI2008]GT考试 矩阵乘法优化DP+KMP
- BZOJ 1009 [HNOI2008]GT考试 ——矩阵乘法 KMP
- [HNOI2008]BZOJ1009 GT考试 - 动态规划 - 矩阵乘法 - KMP
- BZOJ1009 [HNOI2008]GT考试 矩阵
- 【矩阵乘】【KMP】【HNOI 2008】【bzoj 1009】GT考试
- BZOJ.1009.[HNOI2008]GT考试(KMP DP 矩阵快速幂)
- BZOJ1009 [HNOI2008]GT考试【kmp+矩阵加速DP】
- [BZOJ1009]HNOI2008 GT考试|KMP|递推|矩阵乘法
- BZOJ1009 [HNOI2008]GT考试(KMP算法+矩阵加速dp)
- BZOJ 1009([HNOI2008]GT考试-KMP+矩阵加速Dp)