数位DP
题目条件等价于一个数是Beautiful number当且仅当这个数是所有非零数位最小公倍数的倍数。我们先暴力求出1到9任意子集的lcm,发现只有48个。定义状态:dp[i][j][k]表示考虑后i位的lcm是j,和为k的数的个数。注意j这一位需要哈希一下。转移的时候1到9枚举填的数字即可。
#include <bits/stdc++.h>
#define maxn 2520
#define MOD 2520
using namespace std;
int f[5000],id;
int a[maxn],num;
__int64 dp[22][48][MOD];
int gcd(int a,int b)
{
return b?gcd(b,a%b):a;
}
int lcm(int a,int b)
{
return a/gcd(a,b)*b;
}
__int64 dfs(int cur,int prelcm,int sum,bool flag)
{
if(cur==0)
{
return sum%prelcm==0;
}
if(!flag&&dp[cur][f[prelcm]][sum]!=-1)
return dp[cur][f[prelcm]][sum];
int up;
__int64 res=0;
if(flag) up=a[cur];
else up=9;
for(int j=0;j<=up;j++)
{
int now=prelcm;
if(j!=0)
now=lcm(prelcm,j);
res+=dfs(cur-1,now,(10*sum+j)%MOD,flag&&j==up);
}
if(!flag)
dp[cur][f[prelcm]][sum]=res;
return res;
}
__int64 solve(__int64 x)
{
num=0;
while(x)
{
a[++num]=x%10;
x/=10;
}
return dfs(num,1,0,1);
}
set<int>st;
int main()
{
memset(f,-1,sizeof(f));
for(int i=0;i<1<<9;i++)
{
int cur=1;
for(int j=1;j<=9;j++)
{
if(i&(1<<(j-1)))
cur=lcm(cur,j);
}
if(f[cur]==-1)
{
f[cur]=id++;
}
}
int tt;
scanf("%d",&tt);
memset(dp,-1,sizeof(dp));
while(tt--)
{
__int64 L,R;
scanf("%I64d%I64d",&L,&R);
printf("%I64d\n",solve(R)-solve(L-1));
}
system("pause");
return 0;
}