嗯,写完这个题后对数位dp有了一个更清楚的认识
关键是对状态的一些理解
题意就是对l到r区间内,能被他的非零位数整除的数的个数
那么对于每一个第k位,前面数字之和是i,前面位数的最小公倍数是j的状态,他的值是确定的
那么我们就有状态方程dp[k][i][j]了
还有就是我们发现1到9的最大公倍数是2520
那么0到2520中能被2520mod掉的数只有48个
也就是说,1到9组合起来的公倍数只有48个
这样我们就可以愉快的进行离散化了
还有就是dp初始化一次就行了,他的只要是这个状态他的值就是不会改变的
这样不仅省去了初始化的时间还可以节省时间
#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#define ll long long
#define Mod 2520
using namespace std;
int a[50];
ll dp[50][2525][50];
int Hash[2525];
ll gcd(ll a,ll b)
{
if (b==0)
return a;
return gcd(b,a%b);
}
ll lcm(ll a,ll b)
{
return a/gcd(a,b)*b;
}
int cal(ll data)
{
int n(0);
while(data)
{
n++;
a[n]=data%10;
data/=10;
}
return n;
}
ll DFS(int poi,int pre,int dat,int limit)
{
if (poi==0)
return (pre%dat)==0;
if (!limit&&dp[poi][pre][Hash[dat]]!=-1)
return dp[poi][pre][Hash[dat]];
int up;
if (limit)
up=a[poi];
else up=9;
ll ans(0);
for (int k=0;k<=up;k++)
{
int nowp=pre*10+k;
nowp=nowp%Mod;
int nowd=dat;
if (k)
nowd=lcm(nowd,k);
ans+=DFS(poi-1,nowp,nowd,limit&&k==a[poi]);
// if (k!=0)
// ans+=DFS(poi-1,(pre*10+k)%Mod,lcm(dat,k),limit&&k==a[poi]);
//else
// ans+=DFS(poi-1,(pre*10+k)%Mod,dat,limit&&k==a[poi]);
}
if (!limit) dp[poi][pre][Hash[dat]]=ans;
return ans;
}
ll solve(ll data)
{
int n=cal(data);
return DFS(n,0,1,1);
}
int main()
{
int t;
ll l,r;
int cnt(0);
for (int i=1;i<=2520;i++)
{
if ((2520%i)==0)
{
Hash[i]=cnt;
cnt++;
}
}
//cout<<cnt<<endl;
scanf("%d",&t);
memset(dp,-1,sizeof(dp));
while(t--)
{
scanf("%I64d %I64d",&l,&r);
printf("%I64d\n",solve(r)-solve(l-1));
}
return 0;
}