CF 55 D. Beautiful numbers

D. Beautiful numbers

链接

题意:

  求[L,R]中多少个数字可以整除它们的每一位上的数字。

分析:

  要求模一些数字等于0等价于模它们的lcm等于0,所以可以记录当前出现的数字的lcm,最后判断组成的数字是否模lcm等于0。

  但是这个数字太大记录不下。根据一个性质a%b=(a%kb)%b,所以可以记录当前的数字模2520的值,最后模一下lcm。

  这样的状态是$20 \times 2520 \times 2520$的,状态太大了,考虑如何缩小空间。因为1~9的lcm只能是50个左右,所以可以将第三维压成50。

代码:

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<iostream>
#include<cctype>
#include<set>
#include<queue>
#include<vector>
#include<map>
using namespace std;
typedef long long LL;

inline LL read() {
    LL x=0,f=1;char ch=getchar();for(;!isdigit(ch);ch=getchar())if(ch=='-')f=-1;
    for(;isdigit(ch);ch=getchar())x=x*10+ch-'0';return x*f;
}

const int N = 2520;
LL dp[30][50][N + 10];
int num[50]={0,1,2,3,4,5,6,7,8,9,10,12,14,15,18,20,21,24,28,30,35,36,40,42,45,56,60,63,70,72,84,90,105,120,126,140,168,180,210,252,280,315,360,420,504,630,840,1260,2520};
int a[30], pos[N + 10];

int gcd(int a,int b) { return b == 0 ? a : gcd(b, a % b); }
int getlcm(int a,int b) { return a * b / gcd(a, b); }

LL dfs(int p,int lcm,int now,bool lim) {
    if (p == 0) return lcm && now % num[lcm] == 0;
    if (!lim && ~dp[p][lcm][now]) return dp[p][lcm][now];
    int u = lim ? a[p] : 9;
    LL ans = 0;
    for (int i = 0; i <= u; ++i) {
        int t = lcm ? pos[getlcm(num[lcm], max(i, 1))] : i;
        ans += dfs(p - 1, t, (now * 10 + i) % N, lim && i == u);
    }
    if (!lim) dp[p][lcm][now] = ans;
    return ans;    
}
LL solve(LL x) {
    if (x <= 0) return 0;
    int pos = 0;
    while (x) {
        a[++pos] = x % 10; x /= 10;
    }
    return dfs(pos, 0, 0, 1);
}
int main() {
    memset(dp, -1, sizeof(dp));
    for (int i = 0; i < 49; ++i) pos[num[i]] = i;
    for (int T = read(); T --; ) {
        LL x = read(), y = read();
        cout << (solve(y) - solve(x - 1)) << "\n";
    }
    return 0;
}

 

posted @ 2019-03-17 21:48  MJT12044  阅读(174)  评论(0编辑  收藏  举报