数位DP
模板
点击查看代码
#include<functional>
#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<complex>
#include<string>
#include<cstdio>
#include<vector>
#include<cmath>
#include<queue>
#include<deque>
#include<stack>
#include<map>
#include<set>
#define ll long long
#define pa pair<int,int>
#define mp make_pair
#define pb push_back
#define fi first
#define se second
#define YES {puts("YES");return;}
#define NO {puts("NO");return ;}
using namespace std;
const int maxn=2e5+101;
const int MOD=20020219;
const ll inf=2147383647;
const double eps=1e-12;
ll read(){
ll x=0,f=1;char ch=getchar();
for(;!isdigit(ch);ch=getchar())if(ch=='-')f=-1;
for(;isdigit(ch);ch=getchar())x=x*10+ch-'0';
return x*f;
}
int a[21],cnt,tot;
ll dp[13][10][13][2][2][2][2];
ll dfs(int pos,int last,int cnt,int f1,int f2,int f3,int flag,int pre0){
//cnt~pos+1位置前的状态为
//last是pos+1位置的数,cnt为last的连续个数
//f1为是否存在至少连续3个数,f2为是否出现8,f3为是否出现4
//flag=1 表示第i位取值没有限制,为0~9
//flag=0 表示第i位取值有限制,为0~a[i]
//pre0为 是否有前导0
if(pos==0){
return f1 && ((f2 && !f3) || (!f2 && f3) || (!f2 && !f3));
}
if(flag && dp[pos][last][cnt][f1][f2][f3][pre0]!=-1)return dp[pos][last][cnt][f1][f2][f3][pre0];
int limi=flag ? 9: a[pos];
ll ans=0;
for(int i=0;i<=limi;i++){
if(i==last){
if(!i && pre0){
ans+=dfs(pos-1,last,cnt+1,0,f2 || i==8,
f3 || i==4, flag || i<limi, pre0 && !i);
}
else ans+=dfs(pos-1,last,cnt+1,f1 || cnt+1>=3,f2 || i==8,
f3 || i==4, flag || i<limi, pre0 && !i);
}
else {
ans+=dfs(pos-1,i,1,f1,f2 || i==8,
f3 || i==4, flag || i<limi, pre0 && !i);
}
}
if(flag)dp[pos][last][cnt][f1][f2][f3][pre0]=ans;
return ans;
}
ll solve(ll x){
cnt=0;
while(x){
a[++cnt]=x%10;
x/=10;
}
//初始flag=0,pre0=1
return dfs(cnt,0,0,0,0,0,0,1);
};
ll l,r;
int main(){
memset(dp,-1,sizeof(dp));
l=read();r=read();
printf("%lld\n",solve(r)-solve(l-1));
return 0;
}
例题
1.送分了QAQ
注意,对于每次询问给的区间没有什么附加条件的话,dp数组只在开头设为-1即可
点击查看代码
#include<functional>
#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<complex>
#include<string>
#include<cstdio>
#include<vector>
#include<cmath>
#include<queue>
#include<deque>
#include<stack>
#include<map>
#include<set>
#define ll long long
#define pa pair<int,int>
#define mp make_pair
#define pb push_back
#define fi first
#define se second
#define YES {puts("YES");return;}
#define NO {puts("NO");return ;}
using namespace std;
const int maxn=2e5+101;
const int MOD=998244353;
const ll inf=2147383647;
const double eps=1e-12;
ll read(){
ll x=0,f=1;char ch=getchar();
for(;!isdigit(ch);ch=getchar())if(ch=='-')f=-1;
for(;isdigit(ch);ch=getchar())x=x*10+ch-'0';
return x*f;
}
int a[maxn],cnt;
ll dp[maxn][3];
/*
数字:1 4 3 2
位置:4 3 2 1
设dp[pos][st],cnt为数字的位数
dp[i][0]表示cnt~i+1位置不含38或4时,位置i~1的方案数(i~1可能有38或4)
dp[i][1]表示cnt~i+1位置不含38或4,且i+1位置为3时,位置i~1的方案数(i~1可能有38或4)
dp[i][2]表示cnt~i+1位置 含 38或4时,位置i~1的方案数(i~1可能有38或4)
因为数位dp的特性,从高位往低位枚举,我们通过定义枚举过的高位的状态来定义dp数组
例如这道题:
dp[i][st]的st状态是cnt~i+1位置(枚举过的高位)的状态来定义的,与接下来i~1的状态如何无关
*/
ll dfs(int pos,int st,int flag){
//cnt~pos+1位置前的状态为st
//flag=1 表示第i位取值没有限制,为0~9
//flag=0 表示第i位取值有限制,为0~a[i]
if(pos==0)return st==2;
if(flag && dp[pos][st]!=-1)return dp[pos][st];
int limi=flag?9:a[pos];
ll ans=0;
for(int i=0;i<=limi;i++){
if(i==4 || st==2 || (st==1 && i==8)){
ans+=dfs(pos-1,2,flag || i<limi);
}
else if(i==3){
ans+=dfs(pos-1,1,flag || i<limi);
}
else ans+=dfs(pos-1,0,flag || i<limi);
}
if(flag)dp[pos][st]=ans;
return ans;
}
ll solve(ll x){
cnt=0;
while(x){
a[++cnt]=x%10;
x/=10;
}
//对于这道题不用每次都清-1 for(int i=0;i<=cnt;i++)for(int j=0;j<3;j++)dp[i][j]=-1;
return dfs(cnt,0,0);
}
ll l,r;
int main(){
memset(dp,-1,sizeof(dp));
while(scanf("%lld%lld",&l,&r)!=EOF){
if(l==r && r==0)break;
printf("%lld\n",solve(r)-solve(l-1));
}
return 0;
}
2.诡异数字
注意:
这里每个区间的限制条件不同,每次询问都要重新把dp数组设为-1
点击查看代码
#include<functional>
#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<complex>
#include<string>
#include<cstdio>
#include<vector>
#include<cmath>
#include<queue>
#include<deque>
#include<stack>
#include<map>
#include<set>
#define ll long long
#define pa pair<int,int>
#define mp make_pair
#define pb push_back
#define fi first
#define se second
#define YES {puts("YES");return;}
#define NO {puts("NO");return ;}
using namespace std;
const int maxn=2e5+101;
const int MOD=20020219;
const ll inf=2147383647;
const double eps=1e-12;
ll read(){
ll x=0,f=1;char ch=getchar();
for(;!isdigit(ch);ch=getchar())if(ch=='-')f=-1;
for(;isdigit(ch);ch=getchar())x=x*10+ch-'0';
return x*f;
}
int a[maxn],cnt;
ll dp[21][10][101];
int top[11];
ll dfs(int pos,int last,int sum,int flag){
//cnt~pos+1位置前的状态为pos+1位置为last,且连续了sum次
//flag=1 表示第i位取值没有限制,为0~9
//flag=0 表示第i位取值有限制,为0~a[i]
if(pos==0)return 1;
if(flag && dp[pos][last][sum]!=-1)return dp[pos][last][sum];
int limi=flag ? 9: a[pos];
ll ans=0;
for(int i=0;i<=limi;i++){
if(i==last){
if(sum+1>top[i])continue;
ans+=dfs(pos-1,last,sum+1,flag || i<limi);
ans%=MOD;
}
else {
if(1>top[i])continue;
ans+=dfs(pos-1,i,1,flag || i<limi);
ans%=MOD;
}
}
if(flag)dp[pos][last][sum]=ans;
return ans;
}
ll solve(ll x){
if(x<0)return 0;
cnt=0;
while(x){
a[++cnt]=x%10;
x/=10;
}
return dfs(cnt,0,0,0);
}
ll l,r;
int main(){
int t=read();
while(t--){
l=read();r=read();
int n=read();
for(int i=0;i<10;i++)top[i]=20;
for(int i=1;i<=n;i++){
int x=read(),len=read();
top[x]=min(top[x],len);
}
memset(dp,-1,sizeof(dp));
printf("%lld\n",((solve(r)-solve(l-1))%MOD+MOD)%MOD);
}
return 0;
}
3.7的意志
注意题目不是任意,是存在一个数,那么只用看7就行
点击查看代码
#include<functional>
#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<complex>
#include<string>
#include<cstdio>
#include<vector>
#include<cmath>
#include<queue>
#include<deque>
#include<stack>
#include<map>
#include<set>
#define ll long long
#define pa pair<int,int>
#define mp make_pair
#define pb push_back
#define fi first
#define se second
#define YES {puts("YES");return;}
#define NO {puts("NO");return ;}
using namespace std;
const int maxn=2e5+101;
const int MOD=20020219;
const ll inf=2147383647;
const double eps=1e-12;
ll read(){
ll x=0,f=1;char ch=getchar();
for(;!isdigit(ch);ch=getchar())if(ch=='-')f=-1;
for(;isdigit(ch);ch=getchar())x=x*10+ch-'0';
return x*f;
}
int a[maxn],cnt;
ll dp[21][10][10];
int top[11];
ll dfs(int pos,int yu,int sum,int flag){
//cnt~pos+1位置前的状态为
//yu为cnt~pos+1的余数
//sum为cnt~pos+1的数字和
//flag=1 表示第i位取值没有限制,为0~9
//flag=0 表示第i位取值有限制,为0~a[i]
if(pos==0)return yu%7==0 && sum%7==0;
if(flag && dp[pos][yu][sum]!=-1)return dp[pos][yu][sum];
int limi=flag ? 9: a[pos];
ll ans=0;
for(int i=0;i<=limi;i++){
int now_yu=(yu*10+i)%7,now_sum=(sum+i)%7;
ans+=dfs(pos-1,now_yu,now_sum,flag || i<limi);
}
if(flag)dp[pos][yu][sum]=ans;
return ans;
}
ll solve(ll x){
cnt=0;
while(x){
a[++cnt]=x%10;
x/=10;
}
return dfs(cnt,0,0,0);
}
ll l,r;
int main(){
memset(dp,-1,sizeof(dp));
while(1){
l=read();r=read();
if(l==0 && l==r)break;
printf("%lld\n",solve(r)-solve(l-1));
}
return 0;
}
4.Beautiful Numbers
枚举最后序列的和x,每个x进行一次数位dp
数位dp时间复杂度为\(O(状态数*转移数)=O(12*108*108*108*108*10)\)
点击查看代码
#include<functional>
#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<complex>
#include<string>
#include<cstdio>
#include<vector>
#include<cmath>
#include<queue>
#include<deque>
#include<stack>
#include<map>
#include<set>
#define ll long long
#define pa pair<int,int>
#define mp make_pair
#define pb push_back
#define fi first
#define se second
#define YES {puts("YES");return;}
#define NO {puts("NO");return ;}
using namespace std;
const int maxn=2e5+101;
const int MOD=20020219;
const ll inf=2147383647;
const double eps=1e-12;
ll read(){
ll x=0,f=1;char ch=getchar();
for(;!isdigit(ch);ch=getchar())if(ch=='-')f=-1;
for(;isdigit(ch);ch=getchar())x=x*10+ch-'0';
return x*f;
}
int a[15],cnt;
ll dp[14][121][121][121];
ll dfs(int pos,int x,int yu,int sum,int flag){
//cnt~pos+1位置前的状态为
//x是最后序列的的和
//yu为cnt~pos+1的除以x的余数
//sum为cnt~pos+1的数字和
//flag=1 表示第i位取值没有限制,为0~9
//flag=0 表示第i位取值有限制,为0~a[i]
if(sum>x)return 0;
if(pos==0)return x==sum && yu%sum==0;
if(flag && dp[pos][x][yu][sum]!=-1)return dp[pos][x][yu][sum];
int limi=flag ? 9: a[pos];
ll ans=0;
for(int i=0;i<=limi;i++){
int now_yu=(yu*10+i)%x;
ans+=dfs(pos-1,x,now_yu,sum+i,flag || i<limi);
}
if(flag)dp[pos][x][yu][sum]=ans;
return ans;
}
ll solve(ll x,int y){
cnt=0;
while(x){
a[++cnt]=x%10;
x/=10;
}
return dfs(cnt,y,0,0,0);
}
ll l,r;
int main(){
int t=read();
memset(dp,-1,sizeof(dp));
for(int i=1;i<=t;i++){
ll x=read();
ll ans=0;
for(int j=1;j<=108;j++)ans+=solve(x,j);
printf("Case %d: %lld\n",i,ans);
}
return 0;
}
5.美丽数
被所有包含的数整除,如果1~9每个数都记录,那么状态爆炸
不妨考虑最小公倍数,可以将状态压缩,因为最小公倍数的种类很少可以预处理来进一步压缩
同时我们知道1~9的最小公倍数为2520
我们不必枚举数的真正大小,直接枚举数的大小%2520
点击查看代码
#include<functional>
#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<complex>
#include<string>
#include<cstdio>
#include<vector>
#include<cmath>
#include<queue>
#include<deque>
#include<stack>
#include<map>
#include<set>
#define ll long long
#define pa pair<int,int>
#define mp make_pair
#define pb push_back
#define fi first
#define se second
#define YES {puts("YES");return;}
#define NO {puts("NO");return ;}
using namespace std;
const int maxn=2e5+101;
const int MOD=20020219;
const ll inf=2147383647;
const double eps=1e-12;
ll read(){
ll x=0,f=1;char ch=getchar();
for(;!isdigit(ch);ch=getchar())if(ch=='-')f=-1;
for(;isdigit(ch);ch=getchar())x=x*10+ch-'0';
return x*f;
}
int gcd(int a,int b){
if(b>a)swap(a,b);
if(b==0)return a;
return gcd(b,a%b);
}
int a[21],cnt,tot;
ll dp[20][2521][49];
map<int,int>m; //m[i]表示第i个gcd
map<int,int>mm; //mm[i]表示gcd=i的在位置
ll dfs(int pos,int x,int k,int flag){
//cnt~pos+1位置前的状态为
//x是cnt~pos+1所表示的数除以2520的余数
//k为cnt~pos+1的数的lcm=m[k]
//flag=1 表示第i位取值没有限制,为0~9
//flag=0 表示第i位取值有限制,为0~a[i]
if(pos==0)return x%m[k]==0;
if(flag && dp[pos][x][k]!=-1)return dp[pos][x][k];
int limi=flag ? 9: a[pos];
ll ans=0;
for(int i=0;i<=limi;i++){
int now_x=(x*10+i)%2520,now_k=(i==0?m[k]:m[k]*i/gcd(m[k],i));
ans+=dfs(pos-1,now_x,mm[now_k],flag || i<limi);
}
if(flag)dp[pos][x][k]=ans;
return ans;
}
ll solve(ll x){
cnt=0;
while(x){
a[++cnt]=x%10;
x/=10;
}
return dfs(cnt,0,mm[1],0);
};
ll l,r;
int main(){
for(int i=1;i<(1<<9);i++){
int lcm=1;
for(int j=0;j<9;j++){
if(i&(1<<j)){
if(lcm%(j+1))lcm=lcm*(j+1)/gcd(lcm,j+1);
}
}
if(mm[lcm])continue;
m[++tot]=lcm;mm[lcm]=tot;
}
int t=read();
memset(dp,-1,sizeof(dp));
for(int i=1;i<=t;i++){
l=read();r=read();
printf("%lld\n",solve(r)-solve(l-1));
}
return 0;
}
6.好朋友
点击查看代码
#include<functional>
#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<complex>
#include<string>
#include<cstdio>
#include<vector>
#include<cmath>
#include<queue>
#include<deque>
#include<stack>
#include<map>
#include<set>
#define ll long long
#define pa pair<int,int>
#define mp make_pair
#define pb push_back
#define fi first
#define se second
#define YES {puts("YES");return;}
#define NO {puts("NO");return ;}
using namespace std;
const int maxn=2e5+101;
const int MOD=20020219;
const ll inf=2147383647;
const double eps=1e-12;
ll read(){
ll x=0,f=1;char ch=getchar();
for(;!isdigit(ch);ch=getchar())if(ch=='-')f=-1;
for(;isdigit(ch);ch=getchar())x=x*10+ch-'0';
return x*f;
}
int a[21],cnt,tot;
ll dp[20][20][2][2];
ll dfs(int pos,int sum,int ok,int flag,int pre0){
//cnt~pos+1位置前的状态为
//cnt是1~pos+1位置有几个0
//ok是1~pos+1中是否存在007
//flag=1 表示第i位取值没有限制,为0~9
//flag=0 表示第i位取值有限制,为0~a[i]
//pre0为 是否有前导0
if(pos==0)return ok==1;
if(flag && dp[pos][sum][ok][pre0]!=-1)return dp[pos][sum][ok][pre0];
int limi=flag ? 9: a[pos];
ll ans=0;
for(int i=0;i<=limi;i++){
if(i==0){
if(pre0)ans+=dfs(pos-1,0,ok,flag || i<limi,pre0 && !i);
else ans+=dfs(pos-1,sum+1,ok,flag || i<limi,pre0 && !i);
}
else if(i==7){
ans+=dfs(pos-1,sum,ok || sum>=2,flag || i<limi,pre0 && !i);
}
else ans+=dfs(pos-1,sum,ok,flag || i<limi,pre0 && !i);
}
if(flag)dp[pos][sum][ok][pre0]=ans;
return ans;
}
ll solve(ll x){
cnt=0;
while(x){
a[++cnt]=x%10;
x/=10;
}
return dfs(cnt,0,0,0,1);
};
ll l,r;
int main(){
memset(dp,-1,sizeof(dp));
int t=read();
ll ans=0;
while(t--){
l=read();r=read();
ans=ans^(solve(r)-solve(l-1));
}
printf("%lld\n",ans);
return 0;
}
7. [ZJOI2010]COUNT 数字计数
0~9一起考虑会很麻烦,可以每个单独考虑
点击查看代码
#include<functional>
#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<complex>
#include<string>
#include<cstdio>
#include<vector>
#include<cmath>
#include<queue>
#include<deque>
#include<stack>
#include<map>
#include<set>
#define ll long long
#define pa pair<int,int>
#define mp make_pair
#define pb push_back
#define fi first
#define se second
#define YES {puts("YES");return;}
#define NO {puts("NO");return ;}
using namespace std;
const int maxn=2e5+101;
const int MOD=20020219;
const ll inf=2147383647;
const double eps=1e-12;
ll read(){
ll x=0,f=1;char ch=getchar();
for(;!isdigit(ch);ch=getchar())if(ch=='-')f=-1;
for(;isdigit(ch);ch=getchar())x=x*10+ch-'0';
return x*f;
}
int a[21],cnt,tot;
ll dp[20][20][11][2];
ll dfs(int pos,int sum,int id,int flag,int pre0){
//cnt~pos+1位置前的状态为
//cnt是1~pos+1位置有几个id
//flag=1 表示第i位取值没有限制,为0~9
//flag=0 表示第i位取值有限制,为0~a[i]
//pre0为 是否有前导0
if(pos==0)return sum;
if(flag && dp[pos][sum][id][pre0]!=-1)return dp[pos][sum][id][pre0];
int limi=flag ? 9: a[pos];
ll ans=0;
for(int i=0;i<=limi;i++){
if(i==id){
if(id==0){
if(pre0)ans+=dfs(pos-1,sum,id,flag || i<limi,pre0 && !i);
else ans+=dfs(pos-1,sum+1,id,flag || i<limi,pre0 && !i);
}
else ans+=dfs(pos-1,sum+1,id,flag || i<limi,pre0 && !i);
}
else {
ans+=dfs(pos-1,sum,id,flag || i<limi,pre0 && !i);
}
}
if(flag)dp[pos][sum][id][pre0]=ans;
return ans;
}
ll solve(ll x,int id){
cnt=0;
while(x){
a[++cnt]=x%10;
x/=10;
}
return dfs(cnt,0,id,0,1);
};
ll l,r;
int main(){
memset(dp,-1,sizeof(dp));
l=read();r=read();
for(int i=0;i<10;i++){
printf("%lld ",solve(r,i)-solve(l-1,i));
}
return 0;
}
8. [SCOI2012]BLINKER的仰慕者
这道题跟前面的题不一样,这道题要求所有满足要求的数字和,与之前的统计方案数不同
但数字和是在统计方案数的基础上求得的
设\(dp_g[pos][sum]\)表示k除以cnt~pos+1位置的积的值为sum时,满足条件的数字个数
\(dp_g'[pos][sum]\)表示k除以cnt~pos+1位置的积的值为sum时,满足条件的数字和
假设pos位置填的数是i
那么\(dp_g'[pos][sum]+=i*10^{pos-1}*dp_g[pos-1][sum/i]+dp_g'[pos-1][sum/i]\)
那么我们可以用pair把\(dp_g和dp_g'\)包装在一起进行数位dp求解
为什么我们要把sum设为k除以cnt~pos+1位置的积的值,而不是\(cnt~pos+1\)位置的积?
如何设为\(cnt~pos+1\)位置的积,那么记录方案数只能根据特定k,也就是说每次新的k要重新清除dp_g数组的内容,但这样会导致超时
而前者可以重复使用
我们要注意对k=0单独考虑
点击查看代码
#include <bits/stdc++.h>
#define ll long long
#define pa pair<ll,ll>
#define mp make_pair
#define pb push_back
#define fi first
#define se second
#define YES {puts("YES");return;}
#define NO {puts("NO");return ;}
using namespace std;
const int maxn=7e4+101;
const int MOD=20120427;
const ll inf=1e17;
const double eps=1e-12;
ll read(){
ll x=0,f=1;char ch=getchar();
for(;!isdigit(ch);ch=getchar())if(ch=='-')f=-1;
for(;isdigit(ch);ch=getchar())x=x*10+ch-'0';
return x*f;
}
int a[21],cnt,cnt_ji;
pa dp_g0[21][2][2],dp_g[21][maxn][2];
ll l,r,k,ten[21];
unordered_map<ll,int>mm;
pa add(pa a,pa b,ll num,ll pos){
return {(a.fi+b.fi+ten[pos-1]*num%MOD*b.se%MOD)%MOD,(a.se+b.se)%MOD};
}
pa dfs_g(int pos,ll sum,int flag,int pre0){
//cnt~pos+1位置前的状态为
//sum是k除以cnt~pos+1位置的积的值
//dp_g[pos][sum][pre0]的first是数字和,second是方案数
//flag=1 表示第i位取值没有限制,为0~9
//flag=0 表示第i位取值有限制,为0~a[i]
//pre0为 是否有前导0
if(pos==0)return {0,sum==1};
int id=mm[sum];
if(flag && ~dp_g[pos][id][pre0].first)return dp_g[pos][id][pre0];
int limi=flag ? 9: a[pos];
pa ans={0,0};
for(ll i=0;i<=limi;i++){
if(i){
if(sum%i==0)
ans=add(ans,dfs_g(pos-1,sum/i,flag || i<limi,pre0 && !i),i,pos);
}
else if(pre0)ans=dfs_g(pos-1,sum,flag || i<limi, pre0 && !i);
ans.fi%=MOD;ans.se%=MOD;
}
if(flag)dp_g[pos][id][pre0]=ans;
return ans;
}
pa dfs_g0(int pos,ll sum,int flag,int pre0){
//cnt~pos+1位置前的状态为
//sum是cnt~pos+1位置是否有0,不含前导0
//dp_g0[pos][sum][pre0]的first是数字和,second是方案数
//flag=1 表示第i位取值没有限制,为0~9
//flag=0 表示第i位取值有限制,为0~a[i]
//pre0为 是否有前导0
if(pos==0)return {0,sum>0};
if(flag && ~dp_g0[pos][sum][pre0].fi)return dp_g0[pos][sum][pre0];
int limi=flag ? 9: a[pos];
pa ans={0,0};
for(ll i=0;i<=limi;i++){
if(i)
ans=add(ans,dfs_g0(pos-1,sum,flag || i<limi,pre0 && !i),i,pos);
else ans=dfs_g0(pos-1,(sum || !pre0)?1:0,flag || i<limi, pre0 && !i);
}
if(flag)dp_g0[pos][sum][pre0]=ans;
return ans;
}
ll solve(ll x){
if(!x)return 0;
cnt=0;
while(x){
a[++cnt]=x%10;
x/=10;
}
if(k)return dfs_g(cnt,k,0,1).fi;
else return dfs_g0(cnt,0,0,1).fi;
};
vector<ll>nums;
ll p[4]={2,3,5,7};
void get_number(int u, ll t){
if(u==4){
nums.push_back(t);
return;
}
for(int i=0;t<=1e18;i++){
get_number(u+1,t);
t*=p[u];
}
return;
}
int main(){
ten[0]=1;for(int i=1;i<=19;i++)ten[i]=ten[i-1]*10ll%MOD;
get_number(0,1);nums.pb(0);
for(int i=0;i<nums.size();i++)mm[nums[i]]=i+1;//提前预处理出合法的k
memset(dp_g,-1,sizeof(dp_g));
memset(dp_g0,-1,sizeof(dp_g0));
int t=read();
while(t--){
l=read(),r=read(),k=read();
if(!mm.count(k)){puts("0");continue;}//如果k含除2,3,5,7外的质数就不合法
//k=0是合法的
printf("%lld\n",((solve(r)-solve(l-1))%MOD+MOD)%MOD);
}
return 0;
}