51nod 1232 完美数 / codeforces 55D 数位DP

题目来源: 胡仁东
基准时间限制:2 秒 空间限制:131072 KB 分值: 160 难度:6级算法题

如果一个数能够被组成它的各个非0数字整除,则称它是完美数。例如:1-9都是完美数,10,11,12,101都是完美数,但是13就不是完美数(因为13不能被数字3整除)。
现在给定正整数x,y,求x和y之间(包含x和y的闭区间)共有多少完美数。

题目作者为:hrdv
Input
第1行:一个数T,表示后面用作输入测试的数的数量。(1 <= T <= 10000)
第2 - T + 1行:每行2个数,X, Y中间用空格分割。(1 <= X <= Y <= 10^18)
Output
输出共T行,对应区间中完美数的数量。
Input示例
2
1 9
12 15
Output示例
9
2
数位DP经典好题。 首先状态不好想。 dp[i][j][k] 表示到弟i位对2520取模后为j,各位的lcm为k的数字个数。
为什么要这样是因为同余定理(a*10 + b)%2520 =  (a%2520*10 + b) % 2520 。因为1-9的最小公倍数不会超过2520,
那么就可以写出式子。 还有个优化。因为19*2520*2520太大, 会mle, 所以可以优化下第三维,因为最小公倍数都是离散的, 所以可以hash记录下。
#include <bits/stdc++.h>
using namespace std;
int num[20];
long long dp[20][2522][49];
int __lcm(int a, int b){
    return a/__gcd(a, b)*b;
}
int Index[2522];
void init(){
    int te = 1;
    for(int i=1; i<=2520; i++)
        if(2520%i == 0)
            Index[i] = te++;
}
long long dfs(int i, int p, int lcm, bool e) {
    if (i==-1) return p%lcm == 0;
    if (!e && ~dp[i][p][Index[lcm]]) return dp[i][p][Index[lcm]];
    long long res = 0;
    int u = e?num[i]:9;
    for (int d = 0; d <= u; ++d){
        int nowlcm = lcm;
        if(d) nowlcm = __lcm(nowlcm, d);
        res += dfs(i-1, (p*10+d)%2520, nowlcm,e&&d==u);
    }
    return e?res:dp[i][p][Index[lcm]] = res;
}
long long solve(long long x){
    int t = 0;
    while(x){
        num[t++] = x%10;
        x /= 10;
    }
    return dfs(t-1, 0, 1, true);
}
int main(){
    int T;
    cin>>T;
    init();
    memset(dp, -1, sizeof(dp));
    while(T--){
       long long a,b;
       cin>>a>>b;
       cout<<solve(b) - solve(a-1)<<endl;
    }
    return 0;
}



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章