[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

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]$转移。

 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 }
View Code

 

posted @ 2016-06-25 18:18  CtrlCV  阅读(269)  评论(0编辑  收藏  举报