zoukankan      html  css  js  c++  java
  • 美丽数

    美丽数(数位dp)

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

    这道题是典型的数位dp。首先,如果一个正整数能被它所有非零的数位整除,说明其能被非零数位的lcm(最小公倍数)整除。并且,1到9内所有数的最小公倍数是2520。所以如果一个正整数A能被所有非零数位的lcm L整除,说明(amod 2520mod 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
    ", solve(b)-solve(a-1));
        }
        return 0;
    }
    
  • 相关阅读:
    POJ 2240 Arbitrage spfa 判正环
    POJ 3259 Wormholes spfa 判负环
    POJ1680 Currency Exchange SPFA判正环
    HDU5649 DZY Loves Sorting 线段树
    HDU 5648 DZY Loves Math 暴力打表
    HDU5647 DZY Loves Connecting 树形DP
    CDOJ 1071 秋实大哥下棋 线段树
    HDU5046 Airport dancing links 重复覆盖+二分
    HDU 3335 Divisibility dancing links 重复覆盖
    FZU1686 神龙的难题 dancing links 重复覆盖
  • 原文地址:https://www.cnblogs.com/MyNameIsPc/p/7774151.html
Copyright © 2011-2022 走看看