F:
题意:求区间[x , y]中beautiful number的个数,a positive integer number is beautiful if and only if it is divisible by each of its nonzero digits.
分析:一个数能被它的所有非零数位整除,则能被它们的最小公倍数整除,而1到9的最小公倍数为2520,数位DP时我们只需保存前面那些位的最小公倍数就可进行状态转移,到边界时就把所有位的lcm求出了,为了判断这个数能否被它的所有数位整除,我们还需要这个数的值,显然要记录值是不可能的,其实我们只需记录它对2520的模即可,这样我们就可以设计出如下数位DP:dfs(pos,mod,lcm,f),pos为当前位,mod为前面那些位对2520的模,lcm为前面那些数位的最小公倍数,f标记前面那些位是否达到上限,这样一来dp数组就要开到19*2520*2520,明显超内存了,考虑到最小公倍数是离散的,1-2520中可能是最小公倍数的其实只有48个,经过离散化处理后,dp数组的最后一维可以降到48,这样就不会超了。
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<limits.h>
#include<algorithm>
#include<queue>
#include<stack>
#include<vector>
#include<math.h>
#define MOD 2520
__int64 dp[20][MOD][48];
__int64 digit[20];
int cnt,t[200];
int gcd(int a,int b)
{
int temp;
if(b==0)
return a;
if(a<b)
{
temp=a;
a=b;
b=temp;
}
return gcd(b,a%b);
}
int LCM(int a,int b)
{
return a/gcd(a,b)*b;
}
int bs(int x)
{
int mid,min=0,max=cnt;
while(min+1!=max)
{
mid=min+max>>1;
if(t[mid]>x) max=mid;
else min=mid;
}
return min;
}
__int64 dfs(int pos,int mod,int lcm,int limit)
{
int i,num;
if(pos<=0)
return (mod%t[lcm])?0:1;
if(limit==0 && dp[pos][mod][lcm]!=-1)
return dp[pos][mod][lcm];
num=limit?digit[pos]:9;
__int64 ans=0;
for(i=0;i<=num;i++)
{
int nmod=(mod*10+i)%MOD;
int nlcm=lcm;
if(i)
nlcm=bs(LCM(t[lcm],i));
ans+=dfs(pos-1,nmod,nlcm,limit&&i==num);
}
if(!limit)
dp[pos][mod][lcm]=ans;
return ans;
}
void init()
{
cnt=0;
for(int i=1;i<=MOD;i++)
{
if(MOD%i==0)t[cnt++]=i;
}
memset(dp,-1,sizeof(dp));
}
int main()
{
int T,len;
init();
__int64 n,m;
scanf("%d",&T);
while(T--)
{
scanf("%I64d%I64d",&n,&m);
len=0;
while(m)
{
digit[++len]=m%10;
m/=10;
}
__int64 ans2=dfs(len,0,0,1);
n=n-1;
len=0;
while(n)
{
digit[++len]=n%10;
n/=10;
}
__int64 ans1=dfs(len,0,0,1);
printf("%I64d\n",ans2-ans1);
}
}