美丽数

美丽数(数位dp)

如果一个正整数能被它所有非0的数位整除,那么称这个正整数为美丽数。求区间[a,b]之间的美丽数的个数。对于100%的数据,\(1≤a≤b≤10^{18},t≤10\)

这道题是典型的数位dp。首先,如果一个正整数能被它所有非零的数位整除,说明其能被非零数位的lcm(最小公倍数)整除。并且,1到9内所有数的最小公倍数是2520。所以如果一个正整数A能被所有非零数位的lcm L整除,说明\(a\mod 2520\mod L=0\)(感性理解一下即可)。所以dp方程只用记录一个数模2520的值和所有数位的最小公倍数,就可以判断一个状态是否合法。当然,数位dp还要考虑“天花板”的情况。在dp方程中用一倍空间解决即可。

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

typedef long long LL;
LL t, a, b, cnt, mi[10], map[50], gx[50][50], exist[3000];
LL num[21], dp[21][2][2520][50];

LL gcd(LL x, LL y){ return y?gcd(y, x%y):x; }
LL lcm(LL x, LL y){
    if (!x) return y; if (!y) return x;
    return x*y/gcd(x, y); }

LL solve(LL x){
    LL len=0, ans=0; while (x) num[len++]=x%10, x/=10;
    for (LL i=0; i<=(len-1)/2; ++i) swap(num[i], num[len-i-1]);
    memset(dp, 0, sizeof(dp)); dp[0][1][0][0]=1;
    //注意初始化其实是没实际意义的,只是为了方便推出初始状态
    for (int i=0; i<=len; ++i)
        for (int k=0; k<2520; ++k)
            for (int l=0; l<cnt; ++l){
                if (i==len&&k%map[l]==0){
                    ans+=dp[i][0][k][l]+dp[i][1][k][l];
                    continue; }
                if (dp[i][0][k][l]){
                for (int w=0; w<10; ++w)
                    dp[i+1][0][(k*10+w)%2520][w?gx[l][w-1]:l]
                            +=dp[i][0][k][l];
                }
                if(dp[i][1][k][l]){
                for (int w=0; w<num[i]; ++w)
                    dp[i+1][0][(k*10+w)%2520][w?gx[l][w-1]:l]
                            +=dp[i][1][k][l];
                dp[i+1][1][(k*10+num[i])%2520][num[i]?gx[l][num[i]-1]:l]
                        +=dp[i][1][k][l];
                }
            }
    return ans;
}

int main(){
    mi[0]=1; for (LL i=1; i<10; ++i) mi[i]=mi[i-1]<<1;
    scanf("%lld", &t); LL tmp;
    for (LL i=0; i<(1<<9); ++i){
        tmp=0; for (LL j=1; j<10; ++j)
            if (i&mi[j-1]) tmp=lcm(tmp, j);
        exist[tmp]=1;
    }
    for (LL i=1; i<=2520; ++i){ if (!exist[i]) continue;
        exist[i]=cnt; map[cnt++]=i; }
    for (int i=0; i<cnt; ++i) for(int j=0; j<cnt; ++j)
        gx[i][j]=exist[lcm(map[i], map[j])];
    for (LL tt=0; tt<t; ++tt){
        scanf("%lld%lld", &a, &b);
        printf("%lld\n", solve(b)-solve(a-1));
    }
    return 0;
}
posted @ 2017-11-02 20:12  pechpo  阅读(1066)  评论(0编辑  收藏  举报