天天看点

HDU 5564 Clarke and digits 状压dp+矩阵加速

题目链接:

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 }