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;
    }
    
  • 相关阅读:
    ffmpeg通过组播udp推ts流
    C# 多线程总结
    《图解HTTP》6-首部
    官网下载Java连接MySql驱动jar包
    FineReport——JDBC 连接 MySQL 数据库
    serializeArray()方式请求,php获取的方法
    win10无线wifi总是掉线断网
    C# Post请求中Json格式写法
    Layui upload 上传有进度条
    java 基于redis分布式锁
  • 原文地址:https://www.cnblogs.com/MyNameIsPc/p/7774151.html
Copyright © 2011-2022 走看看