题意:求[x,y]内,能被各个非零位整数的数的个数 比如312能被3、1、2整除所以算一个。
思路:首先最小公倍数的话1~9最大的是2520,然后只要顺着到当前位前的lcm是多少dfs下去就好了。
但是显然dp[20][2520][2520]是开不下的,但是注意到,到当前位的lcm是离散的,其实只有48个。
所以需要离散化成 dp[20][2520][50] 。
然后就是注意到当前位前的lcm了,处理会比较麻烦一点。
因为0的时候要跳过,第一位之前是没有的。
我的处理方式就是遇到之前的没有把之前的那位当前是本身这位。
代码:
#include"cstdlib"
#include"cstdio"
#include"cstring"
#include"cmath"
#include"queue"
#include"algorithm"
#include"iostream"
#define eps 1e-8
using namespace std;
int gcd(int x,int y)
{
return y?gcd(y,x%y):x;
}
int num[22],m,lcm[100],used[2700]; //used是数对应的编号,lcm是编号对应的数是多少
__int64 dp[22][2700][50],ten[22];
__int64 dfs(int site,int mod,int lcmid,int f)
{
if(site==0)
{
if(lcmid==-1) return 0;
return mod%lcm[lcmid]?0:1;
}
if(!f&&dp[site][mod][lcmid]!=-1) return dp[site][mod][lcmid];
int len=f?num[site]:9;
__int64 ans=0;
for(int i=0; i<=len; i++)
{
int tep;
if(lcmid!=-1) tep=lcm[lcmid]; //如果不是-1 就说明出现过了 直接赋值
else
{
if(i==0)
{
ans+=dfs(site-1,(mod+i*ten[site])%2520,-1,f&&i==len); //没有出现过又是0的话 跳过
continue; //记得continue
}
tep=i; //不是0的话 就当做本身
if(used[tep]==-1)
{
used[tep]=m;
lcm[m++]=tep;
}
}
tep=i?tep/gcd(tep,i)*i:tep; //不是0的话计算,是0的话就是之前的
if(used[tep]==-1)
{
used[tep]=m;
lcm[m++]=tep;
}
tep=used[tep];
ans+=dfs(site-1,(mod+i*ten[site])%2520,tep,f&&i==len);
}
if(!f&&lcmid!=-1) dp[site][mod][lcmid]=ans;
return ans;
}
__int64 solve(__int64 x)
{
if(x==0) return 0;
int cnt=0;
while(x)
{
num[++cnt]=x%10;
x/=10;
}
return dfs(cnt,0,-1,1);
}
int main()
{
int t;
cin>>t;
memset(dp,-1,sizeof(dp));
m=0;
memset(lcm,0,sizeof(lcm));
memset(used,-1,sizeof(used));
ten[1]=1;
for(int i=2; i<=20; i++) ten[i]=(ten[i-1]*10)%2520;
while(t--)
{
__int64 x,y;
scanf("%I64d%I64d",&x,&y);
printf("%I64d\n",solve(y)-solve(x-1));
}
return 0;
}