/*
f[i][j][a][b] 共有i位 最高位是j 数的余=a 和的余=b
j k _ _ _
(j+k + (...)) = sum = a
k+(...) = sum-j = a-j
(j*10^(i-1)+k*10^(i-2)+...+1) = num = b
k*10^(i-2)+...+1 = num-j*10^(i-1) = b-j*10^(i-1)
子集
f[i-1][k][(a-j*10^(i-1))%7][(b-j)%7]
假设符合条件的数有t个: jA1 -> jAt
Ai为以j开头的后面i-2位数
则和7无关的整数的平方和
= Σ(jAi)^2 (for i in range(1,t))
= (j*10^(i-1))^2 * t + 2 * j*10^(i-1) * (A1+...+At) + ((A1)^2+...+(At)^2)
所以不能只存Ai的平方和
还要存
1 有效数的个数t
2 有效数的和(A1+...+At)
3 有效数的平方和((A1)^2+...+(At)^2)
struct f[i][j][a][b]
{
s0;满足条件的数的个数
s1;满足条件的数的一次方
s2;满足条件的数的二次方
}
用子集更新:
t = v2.s0
v1.s0
+= count(jA1+...jAt)
=t
=v2.s0
v1.s1
+= (jA1+...+jAt)(把j和Ai分离)
= j*10^(i-1) * t(t个j的10^(i-1)次幂) +(jA1+...+jAt)
= j*10^(i-1) * t + v2.s1
v1.s2
+= Σ(jAi)^2 (for i in range(1,t))
= (j*10^(i-1))^2 * t + 2 * j*10^(i-1) * (A1+...+At) + ((A1)^2+...+(At)^2)
= (j*10^(i-1))^2 * v2.s0 + 2 * j*10^(i-1) * v2.s1 + v2.s2
*/
#include<iostream>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
typedef long long LL;
const int N=20,P=1e9+7;
struct F
{
int s0,s1,s2;//满足条件的数的个数,一次方,二次方
}f[N][10][7][7];//共i位 最高位j 数余7 和余7
int power7[N],power9[N];
int T;
LL l,r;
int mod(LL x,int y)
{
return (x%y+y)%y;
}
void init()
{
for(int i=0;i<=9;i++)
if(i!=7)
{
auto& v=f[1][i][i%7][i%7];//
v.s0++;//有效数+1
v.s1+=i;//有效数的和+=i
v.s2+=i*i;//有效数的平方和+=i^2
}
LL power = 10;//j*10^power = j*10^(i-1)
for(int i=2;i<N;i++,power*=10)//遍历位
{
for(int j=0;j<=9;j++)//第i位填j
{
if(j==7) continue;
for(int a=0;a<7;a++)//和的余数
{
for(int b=0;b<7;b++)//数的余数
{
for(int k=0;k<=9;k++)//第i-1位填k
{
if(k==7) continue;
auto &v1 = f[i][j][a][b],&v2=f[i-1][k][mod(a-j*power,7)][mod(b-j,7)];
v1.s0=mod(v1.s0+v2.s0,P);//v1有效数个数+=v2有效数个数
v1.s1=mod(v1.s1+v2.s1+j*(power%P)%P *v2.s0,P);//有效数的和+=i
v1.s2=mod(v1.s2+
v2.s2+
2*j* power%P *v2.s1+
j*j*(power%P)%P*(power%P)%P*v2.s0
,P);//有效数的平方和+=i^2
}
}
}
}
}
power7[0] = 1;
for(int i=1;i<N;i++) power7[i] = power7[i-1] * 10 % 7;
power9[0] = 1;
for(int i=1;i<N;i++) power9[i] = power9[i-1] * 10ll % P;
}
F get(int i,int j,int a,int b)
{
int s0=0,s1=0,s2=0;
for(int x=0;x<7;x++)
{
for(int y=0;y<7;y++)
{
if(x!=a && y!=b)
{
auto v = f[i][j][x][y];
s0 = (s0+v.s0) % P;
s1 = (s1+v.s1) % P;
s2 = (s2+v.s2) % P;
}
}
}
return {s0,s1,s2};
}
int dp(LL n)
{
if(!n) return 0;
LL backup_n = n % P;
vector<int> nums;
while(n) nums.push_back(n%10),n/=10;
int res = 0;
LL last_a = 0,last_b = 0;
//1(jk...)前面 数的余last_a (jk...)
//1.1 合法情况
// last_a*10^(len(jk...)) + (jk...) mod 7!=0
// 当前所有位构成的数(jk...) mod 7 != -last_a*10^(len(jk...)) % 7
//1.2 更新last_a
// 则算上j后 j(k...) 新的last_a = last_a(旧的)*10+j
//2(jk...)前面和的余last_b (jk...)
//2.1 合法情况
// last_b + (jk...) mod 7 !=0
// 当前所有位上数的和(j+k+...) mod 7 !=-last_b % 7
//2.2 更新last_b
// 则算上j后 j(k...) 新的last_b = last_b(旧的)+j
for(int i = nums.size()-1;i>=0;i--)//遍历位
{
int x = nums[i];
for(int j=0;j<x;j++)//遍历当前最高位的值j
{
if(j==7) continue;
int a = mod(-last_a * power7[i+1],7);
int b = mod(-last_b ,7);
auto v = get(i+1,j,a,b);//共i+1位数 最高位j 数余7!=a 和余7!=b
res = mod(
res
+(last_a%P)*(last_a%P)%P * power9[i+1]%P*power9[i+1]%P*v.s0%P
+2*last_a%P*power9[i+1]%P*v.s1
+v.s2
,P);
}
if(x==7) break;//如果当前位为7 则不能继续往右走
last_a = last_a*10+x;//a(n-1)
last_b +=x;
// 最右子树 所有位都取a(i) = 数本身
// 首先在前两行判断了x!=7
// 那么只要前面的数的余last_a和前面的和的余last_b % 7 != 0
// 求所有数的平方和+数n本身的平方
//
if(!i && last_a%7 && last_b % 7) res=(res+backup_n*backup_n)%P;
}
return res;
}
int main()
{
cin >> T;
init();
while(T--)
{
//这里不用像之前题一样对新数据做memset(f)
cin >> l >> r;
cout << mod(dp(r) - dp(l-1),P) << endl;
}
return 0;
}