[BZOJ1009] [HNOI2008] GT考试 (KMP & dp & 矩阵乘法)
Description
阿申准备报名参加GT考试,准考证号为N位数X1X2....Xn(0<=Xi<=9),他不希望准考证号上出现不吉利的数字。
他的不吉利数学A1A2...Am(0<=Ai<=9)有M位,不出现是指X1X2...Xn中没有恰好一段等于A1A2...Am. A1和X1可以为0
Input
第一行输入N,M,K.接下来一行输入M位的数。 N<=10^9,M<=20,K<=1000
Output
阿申想知道不出现不吉利数字的号码有多少种,输出模K取余的结果.
Sample Input
4 3 100
111
111
Sample Output
81
HINT
Source
Solution
$f[i][j]$表示由$i$的数字构成的数串中与不吉利数字刚好匹配$j$位的方案数
因为我们只需关心后$j$位是否被匹配,如果在不久的将来有一位失配,那么这一段字符就一定不是给定的数串
可以看出来,由$f[i]$转移到$f[i+1]$时,需要借助$KMP$算出,具体方法就是枚举$0\sim 9$,用$\pi$数组算出匹配的位数$k$,$f[i+1][k]$+=$f[i][j]$
注意到准考证号位数可以达到$10^9$妈蛋考试时号码都抄不完,$TLE/MLE$无疑
由于每一次$f[i]$转移到$f[i+1]$时所有步骤是一样的,所以可以用一个矩阵,用乘法代替一次转移。这样就可以用快速幂优化转移啦
构造矩阵暴力即可,注意$f[i][m]$的特殊性,它每次只向$f[i+1][m]$转移。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <bits/stdc++.h> 2 using namespace std; 3 int mod, pi[25]; 4 struct matrix 5 { 6 int a[25][25], n, m; 7 8 matrix() {} 9 10 matrix(int x, int y) 11 { 12 n = x, m = y; 13 memset(a, 0, sizeof(a)); 14 } 15 16 matrix operator* (matrix rhs) 17 { 18 matrix ans(n, rhs.m); 19 for(int i = 0; i < n; ++i) 20 for(int j = 0; j < rhs.m; ++j) 21 for(int k = 0; k < m; ++k) 22 ans.a[i][j] = (ans.a[i][j] + a[i][k] * rhs.a[k][j]) % mod; 23 return ans; 24 } 25 26 matrix operator^ (int rhs) 27 { 28 matrix ans(n, n), bs = *this; 29 for(int i = 0; i < n; ++i) 30 ans.a[i][i] = 1; 31 for(; rhs; rhs >>= 1, bs = bs * bs) 32 if(rhs & 1) ans = ans * bs; 33 return ans; 34 } 35 }; 36 char s[25]; 37 38 void getpi(int m) 39 { 40 int j = 0; 41 for(int i = 2; i <= m; ++i) 42 { 43 while(j && s[i] != s[j + 1]) 44 j = pi[j]; 45 pi[i] = s[i] == s[j + 1] ? ++j : j; 46 } 47 } 48 49 int main() 50 { 51 int n, m, ans, bs = 10; 52 cin >> n >> m >> mod >> s + 1; 53 getpi(m); 54 matrix f(1, m + 1), w(m + 1, m + 1); 55 f.a[0][0] = 1, w.a[m][m] = 10; 56 for(int i = 0; i < m; ++i) 57 for(int j = 0; j < 10; ++j) 58 { 59 int k = i; 60 while(k && j != s[k + 1] - 48) 61 k = pi[k]; 62 if(j == s[k + 1] - 48) ++k; 63 ++w.a[i][k]; 64 } 65 f = f * (w ^ n); 66 for(ans = 1; n; n >>= 1, bs = (bs * bs) % mod) 67 if(n & 1) ans = (ans * bs) % mod; 68 cout << (ans - f.a[0][m] + mod) % mod << endl; 69 return 0; 70 }