zoukankan      html  css  js  c++  java
  • CF .Beautiful numbers 区间有多少个数字是可以被它的每一位非零位整除。(数位DP)

    题意:数字满足的条件是该数字可以被它的每一位非零位整除。

    分析:大概的思路我是可以想到的 , 但没有想到原来可以这样极限的化简 , 在数位dp 的道路上还很长呀 ; 

    我们都知道数位dp 的套路 , 核心的部分就是找到判断这个数的满足条件的方法 , 如果找到了那这个问题就迎刃而解了吧 ; 

    这个题的条件是数字被每一位非零的数整除,那是不是这个是应该被每一位的最小公倍数整除  ,这里是这道题目的关键!!!!!!!!  1-9的最小公倍数是2520 , 所以其他位数最公倍数都是在2520内的(看这样考虑的话 dp数组只要开2520就好了 ,牛逼!!!

    dfs( pos , num , lcm , limit) : 在第几位 , 当前的数字,当前数字所有非零位的最小公倍数,是否限制。

    下面关键的地方又来了: 因为我们枚举完判断是num%lcm是否为0 , 所以!!每次传入num都%2520!! ,这样数组又可以开小了许多 ;

    但是因为我们的dp[pos][num][lcm] , 19*2520*2520 , 这个是无法开的 , 那我们这么办呢??

    又是一个关键!这里居然用到离散化 !! 牛逼! 所以对于lcm这一维我们需要进行离散化,经过打表可以发现,2520内可以整除2520的只有48个,所以我们可以离散化一下让lcm映射到1-48既可以了这样就可以开19x2520x48大小的了。1-2520中可能是最小公倍数的其实只有48个,经过离散化处理后,dp数组的最后一维可以降到48,这样就不会超了

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    using namespace std;
    typedef long long ll;
    const int MOD = 2520;
    int Hash[3000];
    int digit[50];
    ll dp[50][2530][50];
    void init(){
        int cnt = 0;
        for(int i = 1; i <= MOD; i++){
            if(MOD % i == 0)
                Hash[i] = cnt++;
        }
    }
    ll gcd(ll a,ll b){
        if(!b)
            return a;
        else return gcd(b,a%b);
    }
    ll dfs(int pos,int num,int lcm,int limit){
        if(pos == -1)
            return num % lcm == 0;
        ll &dpnow = dp[pos][num][Hash[lcm]];
        if(!limit && dpnow != -1)
            return dpnow;
        int max_digit = limit ? digit[pos] : 9;
        ll ans = 0;
        for(int i = 0; i <= max_digit; i++){
            ans += dfs((pos - 1), ((num * 10 + i) % MOD), (!i ? lcm : lcm * i / gcd(lcm,i)), (limit && i == max_digit));
        }
        if(!limit) dpnow = ans;
        return ans;
    }
    ll solve(ll n){
        int pos = 0;
        while(n){
            digit[pos++] = n % 10;
            n /= 10;
        }
        return dfs(pos-1,0,1,1);
    }
    int main(){
        init();
        int t;
        cin >> t;
        memset(dp,-1,sizeof(dp));
        while(t--){
            ll l,r;
            cin >> l >> r;
            cout << solve(r) - solve(l-1) << endl;
        }
        return 0;
    }
    View Code

     

  • 相关阅读:
    【流程图绘制】+Visual Paradigm+各种常用流程图绘制
    【数据库】+visual paradigm+设计ER数据库模型
    【Java】+正则
    【前端】
    【selenium】+java+谷歌+实现UI自动化
    【前端】+前、后端通过json接口建立连接
    【Java】+前、后端通过json接口建立连接
    【前端】+可视化布局工具
    【jmeter】+Not able to find Java executable or version. Please check your Java installation
    【Git】+常用命令大全
  • 原文地址:https://www.cnblogs.com/shuaihui520/p/9929223.html
Copyright © 2011-2022 走看看