版权声明:欢迎转载,若转载,请标明出处,如有错误,请指点,也欢迎大佬们给出优化方法 https://blog.csdn.net/Charles_Zaqdt/article/details/86531399
题目链接:http://codeforces.com/contest/55/problem/D
题意就是给你一个范围,问这个范围内有多少个数是它各位非零数的倍数。
思路就是数位dp,但是要求这个数要能整除各个非零位,这个状态不太好标记,所以这里就需要用一点数学知识了,一个数可以整出这个数的每一位非零数,那么只要可以整出每一位非零数的lcm就好了,然后我们可以算出1-9的lcm是2520,所以我们只需要去标记每个数的每一位数的lcm的状态就好了。这样的空间复杂度就缩小到了20*2520*2520,但是这样还是很大,然后我们发现1-9的lcm的个数并不多,所以这里又可以用离散化优化到20*30*2520,然后跑一遍就可以了。
AC代码:
#include <bits/stdc++.h>
#define ll long long
using namespace std;
int a[20];
ll dp[20][50][2550];
int Hash[2550];
void init(){
int cnt = 0;
memset(dp, -1, sizeof(dp));
for(int i=1;i<=2520;i++){
if(2520 % i == 0) Hash[i] = cnt++;
}
}
ll lcm(ll x, ll y){
return x / __gcd(x, y) * y;
}
ll dfs(int pos, int prelcm, int prenum, bool limit){
if(pos == -1) return prenum % prelcm == 0;
if(!limit && dp[pos][Hash[prelcm]][prenum] != -1)
return dp[pos][Hash[prelcm]][prenum];
int up = limit ? a[pos] : 9;
ll ans = 0;
for(int i=0;i<=up;i++){
int nownum = (prenum * 10 + i) % 2520;
int nowlcm = prelcm;
if(i) nowlcm = lcm(prelcm, i);
ans += dfs(pos - 1, nowlcm, nownum, limit && i == up);
}
if(!limit) dp[pos][Hash[prelcm]][prenum] = ans;
return ans;
}
ll solve(ll x){
int pos = 0;
while(x){
a[pos++] = x % 10;
x /= 10;
}
return dfs(pos - 1, 1, 0, true);
}
int main()
{
int T;
scanf("%d",&T);
init();
while(T--){
ll l, r;
scanf("%lld%lld",&l, &r);
printf("%lld\n", solve(r) - solve(l - 1));
}
return 0;
}