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 }