传送门:
http://acm.hdu.edu.cn/showproblem.php?pid=5564
题意:
求长度在l到r之间,能被7整除,且相邻数位和不是k的数的个数?
思路:
很明显的dp题目,需要记录的是位数,(数位和)mod7的值,以及最后一位的值。
这样状态转移方程式倒是很好写,但是
1e9*7*10复杂度太高了,所以需要用矩阵快速幂去优化!!!
也是蛮好想的,定义A是状态矩阵,它实际有用的只是其中的1*70,表示到达70种状态分别有多少种情况,那么很显然要求的就是第0到9列的值的和
(定义i*10+j表示的是余数为i,尾数为j的状态),那么B矩阵就为状态转移矩阵喽,i行j列表示的就是从i状态转移到j状态的方法数,然后具体加的时候有个小技巧,就是在B上作一点小手脚,在B中新增加一列,0到9行均为1,再把B矩阵的最后一行最后一列置为1,那么答案矩阵的第0行最后一列表示的就是前缀矩阵的前10列的和喽,(递推往下想一想,是不是很神奇!)
这样就可以完美地解决这个问题了!
code:
#include<bits/stdc++.h>
using namespace std;
const int maxn = 71;
const int mod = 1e9 + 7;
struct Matrix
{
int mat[maxn][maxn];
Matrix(){memset(mat, 0, sizeof(mat));}
void clear(){ memset(mat, 0, sizeof(mat)); }
friend Matrix operator *(const Matrix &A, const Matrix &B);
friend Matrix operator ^(Matrix A, int n);
};
Matrix res1, res2;
Matrix operator *(const Matrix &A, const Matrix &B)
{
Matrix ret;
for (int i = 0; i < maxn; i++)
{
for (int j = 0; j < maxn; j++)
{
for (int k = 0; k < maxn; k++)
{
ret.mat[i][j] = (ret.mat[i][j] + (1LL*A.mat[i][k] * B.mat[k][j]) % mod) % mod;
}
}
}
return ret;
}
Matrix operator ^(Matrix A, int n)
{
Matrix ret;
for (int i = 0; i < maxn; i++)
{
ret.mat[i][i] = 1;
}
for (; n; n >>= 1, A = A*A)
if (n & 1)
ret = ret*A;
return ret;
}
inline int statu(int i, int j)
{
return i * 10 + j;
}
int main()
{
int t;
int i, j, L, R, K, x;
Matrix A, B;
for (i = 1; i < 10; i++)
A.mat[0][statu(i % 7, i)] = 1;
scanf("%d", &t);
while (t--)
{
scanf("%d%d%d", &L, &R, &K);
B.clear();
for (i = 0; i < 7; i++)
{
for (j = 0; j < 10; j++)
{
for (x = 0; x < 10; x++)
{
if (j + x != K)
{
B.mat[statu(i, j)][statu((i * 10 + x) % 7, x)] = 1;
}
}
}
}
for (i = 0; i < 10; i++)
B.mat[i][maxn - 1] = 1;
B.mat[maxn - 1][maxn - 1] = 1;
res1 = A*(B^R);
res2 = A * (B ^ (L - 1));
printf("%d\n", (res1.mat[0][maxn - 1] - res2.mat[0][maxn - 1] + mod) % mod);
}
}