这几天在学数位dp,今天终于差不多弄清楚了数位dp的原理。
数位dp就是把一个数按位从最高位往前递归,按不同的状态来求个数,用dp存下求过的状态,下次就可以直接调用,不用再递归去找,这样就可以节省时间。
这道题就需要知道一个数可以被它所有的非零位整除的话首先要可以被1~9的公倍数(2550)整除,那么保存状态的时候就只要保存%2550就好了.
#include<bits/stdc++.h>
using namespace std;
#define LL long long
const int LCM = 2520;
int num[20],c[2550]; //c数组用来离散化
LL dp[20][50][2550];
void init() {
int cnt = 0;
for(int i = 1;i <= LCM;i++)
if(LCM % i == 0) {
c[i] = cnt++;
}
}
LL dfs(int i,int prelcm,int presum,bool e) { //prelcm当前数字每一位非零数的公倍数,presum当前数%2550的结果,e是否是边界
if(i == 0) return presum % prelcm == 0;
if(!e && dp[i][c[prelcm]][presum] != -1) return dp[i][c[prelcm]][presum];
int u = e ? num[i] : 9;
LL ans = 0;
for(int j = 0;j <= u;j++)
ans += dfs(i-1,j ? prelcm/(__gcd(prelcm,j))*j : prelcm,(presum*10+j) % LCM,e && j == u);
return e ? ans : dp[i][c[prelcm]][presum] = ans;
}
LL slove(LL x) {
int t = 0;
while(x) {
num[++t] = x%10;
x /= 10;
}
return dfs(t,1,0,1);
}
int main() {
init();
int t;
scanf("%d",&t);
memset(dp,-1,sizeof(dp));
while(t--) {
LL a,b;
scanf("%I64d%I64d",&a,&b);
printf("%I64d\n",slove(b) - slove(a-1));
}
}
再做几道题多理解理解。