Problem Description
Clarke is a patient with multiple personality disorder. One day, Clarke turned into a researcher, did a research on digits.
He wants to know the number of positive integers which have a length in [l,r] and are divisible by 7 and the sum of any adjacent digits can not be k.
Input
The first line contains an integer T(1≤T≤5), the number of the test cases.
Each test case contains three integers l,r,k(1≤l≤r≤109,0≤k≤18).
Output
Each test case print a line with a number, the answer modulo 109+7.
Sample Input
2
1 2 5
2 3 5
Sample Output
13
125
Hint:
At the first sample there are 13 number
7,21,28,35,42,49,56,63,70,77,84,91,98
7
,
21
,
28
,
35
,
42
,
49
,
56
,
63
,
70
,
77
,
84
,
91
,
98
satisfied.
题意:
求长度在l到r区间内的数,相邻的两位数加起来不等于ban,总的整个数要能被7整除,这样的数有多少个。
思路:
话说一拿到这道题我首先想到的居然是去查能被7整除的性质,然后成功的发现了我是一个idiot。
然后开始正经的推DP公式,因为没有专项练习过数位DP,所以推得很吃力,不过总算是推出来了
f[i][j][k] 表示长度为i的,能被7整除的,最后一位是k的方案数。
因为知道是矩阵快速幂,所以死命的往矩阵上面凑,然而这里除了i这一维可以快速幂搞掉的,还有两维,跟平常的不一样,于是我才思枯竭。。。
于是高端操作1出现了:f[i][j*10+k]表示的就是f[i][j][k],有点类似状态压缩,剪掉了一维,然后对于这个数组,就可以愉快地使用矩阵快速幂了。
然而因为求的是l到r的区间内的和,所以想到用前缀和一样的东西,最后相减。我最开始是把有前缀0的数也当做一个方案,然后顺利WA掉,于是转向常规做法题解
题解中高端操作2出现了:把求和写在矩阵乘法里面,在原来70*70的转移矩阵之外再开一行一列,而在答案矩阵中多开一列,答案矩阵最后那一列记录前面所有矩阵的所有mod 7 == 0的方案数的和(即前缀和)。
于是这道题解决了,以上是小越越做这道题的坎坷的心路历程。其实打起来只要思路清晰是非常快的,一些细节要在平常打题的时候就养成好习惯,这样就不会出现一些奇怪的根本不知道如何调试的错误了,然后打起来越快的方法越好,比如有一些初始化没必要写在结构体里面,有的时候重载运算符也是冗余的。
上代码,看起来还比较简洁吧:
#include<iostream>
#include<cstdio>
#include<cstring>
#define ll long long
using namespace std;
const ll MOD = 1e9+7;
const int N = 71;
struct MATRIX{
ll a[N][N];
}a, b, ml, mr;
int T, l, r, ban;
MATRIX Mul(MATRIX x, MATRIX y)
{
MATRIX z;
memset(z.a, 0, sizeof(z.a));
for (int i = 0; i < N; i++)
for (int j = 0; j < N; j++)
for (int k = 0; k < N; k++)
z.a[i][j] = (z.a[i][j]+x.a[i][k]*y.a[k][j])%MOD;
return z;
}
MATRIX Pow(MATRIX x, int y)
{
MATRIX z;
memset(z.a, 0, sizeof(z.a));
for (int i = 0; i < N; i++)
z.a[i][i] = 1;
while (y > 0){
if (y&1) z = Mul(z, x);
x = Mul(x, x);
y >>= 1;
}
return z;
}
int main()
{
cin >> T;
memset(a.a, 0, sizeof(a.a));
for (int i = 1; i < 10; i++)
a.a[0][i%7*10+i] = 1;
while (T--){
cin >> l >> r >> ban;
memset(b.a, 0, sizeof(b.a));
for (int i = 0; i < 7; i++)
for (int j = 0; j < 10; j++)
for (int k = 0; k < 10; k++)
if (j+k != ban)
b.a[i*10+j][(i*10+k)%7*10+k] = 1;
for (int i = 0; i < 10; i++)
b.a[i][N-1] = 1;
b.a[N-1][N-1] = 1;
ml = Mul(a, Pow(b, l-1));
mr = Mul(a, Pow(b, r));
cout << (mr.a[0][N-1]-ml.a[0][N-1]+MOD)%MOD << endl;
}
return 0;
}