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;
}



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