题目链接:
http://acm.hdu.edu.cn/showproblem.php?pid=5564
题意:
求长度在[L,R]范围,并且能整除7的整数的总数。
题解:
考虑最原始的想法:
dp[i][j][k]表示长度为i,并且对7取模得到j的以k结尾的数。
则有状态转移方程dp[i+1][(h*10)+l)%7][k]+=dp[i][h][k'](k+k'!=K).
但是i范围是1~10^9,需要矩阵加速。
这里对dp[i][j][k]的[j][k]两个状态进行压缩,得到转移矩阵mat[70][70],其中mat[s1][s2]表示由状态s2变到状态s1的可行性。
并且由于dp[i][j][k]记录的是长度为i的情况,而我们要求的是所有长度小于等于i的前缀和,所以,我们还要加一行一列来计算所有模7等于零的数的个数。
代码:
1 #include<iostream>
2 #include<cstring>
3 #include<cstdio>
4 using namespace std;
5
6 const int maxn = 71;
7 const int mod = 1e9 + 7;
8 typedef long long LL;
9
10 struct Matrix {
11 LL mat[maxn][maxn];
12 Matrix() { memset(mat, 0, sizeof(mat)); }
13 friend Matrix operator *(const Matrix& A, const Matrix& B);
14 friend Matrix operator ^(Matrix A, int n);
15 };
16
17 Matrix operator *(const Matrix& A, const Matrix& B) {
18 Matrix ret;
19 for (int i = 0; i < maxn; i++) {
20 for (int j = 0; j < maxn; j++) {
21 for (int k = 0; k < maxn; k++) {
22 ret.mat[i][j] += A.mat[i][k] * B.mat[k][j] % mod;
23 ret.mat[i][j] %= mod;
24 }
25 }
26 }
27 return ret;
28 }
29
30 Matrix operator ^(Matrix A, int n) {
31 Matrix ret;
32 for (int i = 0; i < maxn; i++) ret.mat[i][i] = 1;
33 while (n) {
34 if (n & 1) ret = ret*A;
35 A = A*A;
36 n /= 2;
37 }
38 return ret;
39 }
40
41 int L, R, K;
42
43 int main() {
44 int tc;
45 scanf("%d", &tc);
46 while (tc--) {
47 scanf("%d%d%d", &L, &R, &K); L--;
48 Matrix A, B;
49 //转移矩阵
50 for (int i = 0; i < 70; i++) {
51 for (int j = 0; j < 70; j++) {
52 int x1 = i / 10, y1 = i % 10;
53 int x2 = j / 10, y2 = j % 10;
54 if (y1 + y2 == K) continue;
55 if ((x2*10 + y1) % 7 == x1) A.mat[i][j] = 1;
56 }
57 }
58 //计算前缀和
59 for (int i = 0; i < 10; i++) A.mat[70][i] = 1;
60 A.mat[70][70] = 1;
61
62 //初始向量
63 for (int i = 1; i < 10; i++) B.mat[(i%7)*10+i][0]++;
64
65 Matrix AR = A^R, AL = A^L;
66 Matrix BR = AR*B, BL = AL*B;
67 LL ans = BR.mat[70][0] - BL.mat[70][0];
68 printf("%lld\n", (ans%mod+mod)%mod);
69 }
70 return 0;
71 }