Description
给定 ([l,r]),求 ([l,r]) 之间有多少个数满足它的任意数位都可以整除它本身。
Solution
(f(i,j,k,full)) 表示从高到低考虑到第 (i) 位数,前面构成的数模 (2520=j),前面这些数位的 (LCM) 为 (k),当前是否贴合上界的方案数。把 (k) 离散化一下节约空间。
注意只需要记录 full=0 的情况即可,并且这些情况可以在各组数据间复用,因此就要求 (i) 要倒着记录。
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 1000005;
vector <int> a;
int mp[2525],imp[2525];
int f[20][2520][50],n;
int gg[55][55];
int CNT;
int gcd(int p,int q)
{
p=mp[p];
q=mp[q];
if(!(~gg[p][q]))
{
++CNT;
return gg[p][q]=__gcd(imp[p],imp[q]);
}
else
{
return gg[p][q];
}
}
int lcm(int p,int q)
{
if(p==0) return q;
if(q==0) return p;
return p*q/gcd(p,q);
}
int calc(int i,int j,int k,int full)
{
CNT++;
if(i<0) return j%imp[k]==0;
if(!full && ~f[i][j][k]) return f[i][j][k];
int lim=full?a[i]:9;
int ans=0;
for(int now=0;now<=lim;now++)
{
int newj=(j*10+now)%2520;
int newk=mp[lcm(now,imp[k])];
ans+=calc(i-1,newj,newk,full&&now==lim);
}
if(!full) return f[i][j][k]=ans;
else return ans;
}
int solve(int n)
{
int ans=0;
a.clear();
// memset(f,-1,sizeof f);
while(n)
{
a.push_back(n%10);
n/=10;
}
::n=a.size();
if(a.size()==0) return 1;
// reverse(a.begin(),a.end());
int tmp = calc(::n-1,0,1,1);
return tmp;
}
void doit()
{
int l,r;
cin>>l>>r;
cout<<solve(r)-solve(l-1)<<endl;
}
signed main()
{
ios::sync_with_stdio(false);
int ind=0;
for(int i=1;i<=2520;i++)
{
if(2520%i==0)
{
mp[i]=++ind;
imp[ind]=i;
}
}
memset(gg,-1,sizeof gg);
memset(f,-1,sizeof f);
int t;
cin>>t;
while(t--)
{
doit();
}
// cout<<CNT<<endl;
}