另
fi,j,k,s
表示DP了前i位,数位和%7的余数为j,前i位的数字%7余数为k,前s位是否与上限相同 的方案数。
gi,j,k,s
一样,但表示的是所有情况的平方和。
hi,j,k,s
表示所有情况的和
三个数组同时转移就可以了。
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
typedef long long ll;
const int N=30,P=1e9+7;
int t;
ll l,r,pw[20];
int g[N][10][10][2],h[N][10][10][2],f[N][10][10][2];
inline void add(int &x,int y){
if((x+=y)>=P) x-=P;
}
inline int calc(ll n){
memset(g,0,sizeof(g));
memset(h,0,sizeof(h));
memset(f,0,sizeof(f));
f[20][0][0][1]=1;
for(int i=20;i>1;i--)
for(int j=0;j<7;j++)
for(int k=0;k<7;k++)
for(int s=0;s<=1;s++)
if(s==0){
if(!f[i][j][k][s]) continue;
for(int d=0;d<=9;d++){
if(d==7) continue;
add(f[i-1][(j+d)%7][(k*10+d)%7][0],f[i][j][k][0]);
add(h[i-1][(j+d)%7][(k*10+d)%7][0],(100LL*h[i][j][k][0]+20LL*g[i][j][k][0]*d+1LL*f[i][j][k][0]*d*d)%P);
add(g[i-1][(j+d)%7][(k*10+d)%7][0],(10LL*g[i][j][k][0]+1LL*f[i][j][k][0]*d)%P);
}
}
else{
for(int d=0;d<(n/pw[i-1])%10;d++){
if(d==7) continue;
add(f[i-1][(j+d)%7][(k*10+d)%7][0],f[i][j][k][s]);
add(h[i-1][(j+d)%7][(k*10+d)%7][0],(100LL*h[i][j][k][s]+20LL*g[i][j][k][s]*d+1LL*f[i][j][k][s]*d*d)%P);
add(g[i-1][(j+d)%7][(k*10+d)%7][0],(10LL*g[i][j][k][s]+1LL*f[i][j][k][s]*d)%P);
}
int d=(n/pw[i-1])%10;
if(d==7) continue;
add(f[i-1][(j+d)%7][(k*10+d)%7][1],f[i][j][k][s]);
add(h[i-1][(j+d)%7][(k*10+d)%7][1],(100LL*h[i][j][k][s]+20LL*g[i][j][k][s]*d+1LL*f[i][j][k][s]*d*d)%P);
add(g[i-1][(j+d)%7][(k*10+d)%7][1],(10LL*g[i][j][k][s]+1LL*f[i][j][k][s]*d)%P);
}
int ret=0;
for(int i=1;i<7;i++)
for(int j=1;j<7;j++)
add(ret,(h[1][i][j][0]+h[1][i][j][1])%P);
return ret;
}
int main(){
scanf("%d",&t);
pw[1]=1; for(int i=2;i<=19;i++) pw[i]=pw[i-1]*10;
while(t--){
scanf("%lld %lld\n",&l,&r);
printf("%d\n",(calc(r)+P-calc(l-1))%P);
}
return 0;
}