zoukankan      html  css  js  c++  java
  • NOIP赛前集训营-提高组(第一场)#B 数数字

    题目描述

    小N对于数字的大小一直都有两种看法。第一种看法是,使用字典序的大小(也就是我们常用的判断数字大小的方法,假如比较的数字长度不同,则在较短一个前面补齐前导0,再比较字典序),比如43<355,10<11。第二种看法是,对于一个数字,定义他的权值为,也就是各个数位的乘积。
    现在给定两个区间,[L,R]与[L1,R1]。小N现在想知道,有多少使用字典序判大小法在[L,R]之间的数字,满足其第二种定义的权值也在[L1,R1]之间。
    换句话说,对于一个数x,定义f(x)为x的各个数位的乘积。对于L<=x<=R,问有多少x满足,L1<=f(x)<=R1。

    输入描述:

    第一行四个整数L,R,L1,R1。

    输出描述:

    一行一个整数,代表小N想知道的数的数量。
    示例1

    输入

    复制
    34 10000 24 57

    输出

    复制
    777

    备注:

    20%: L,R <= 10000000
    40%: L,R <= 3*10^7
    60%: L,R,L1,R1 <= 10^9
    另外有20%:L1,R1<=1000
    100%: 0<=L,R,L1,R1 <= 10^18, L <= R, L1 <= R1

    Solution:

      本题出题人神犇WWT说是经典的数位dp,,,考试时谁都知道是数位dp吖,当时脑抽感觉乘积的情况太多,定义状态怕炸空间,然后就GG了。

      因为数最多有18位,每位除0外有9种填数情况,开始认为状态最多$18*9^{18}$,即使有重复的乘积我也觉得空间开不下,所以弃疗直接裸暴力。然而结果就是状态数其实不大,重复的状态很多很多,完全可以开个map来定义状态,这里我想吐槽了

      于是定义状态$f[i][j]$表示到了第$i$位乘积为$j$的不受限制的合法个数(用map存),然后就是经典的数位记忆化搜索了,注意限制条件决定了从高位向低位搜索,另一个小细节就是有可能合法个数为0,而状态的初值也为0,这样就不方便判断当前状态是否已经搜索过了,简单的解决办法是记录状态的合法个数时+1,取出状态时-1就好了。

      最后简单讲下神犇WWT的绝妙思路:由于只包含1到9,所以只用记录乘积的质因数分解中,出现了多少2,3,5,7。题目中的$L1,R1$不超过$10^{18}$,可以计算出2最多为59个,3最多为37, 5最多为26,7最多为21。设$F[i][c_2][c_3][c_5][c_7]$表示考虑到第i位,乘积状态为$c_2,c_3,c_5,c_7$的数位DP情况。接着就是经典数位DP做法。空间可能比较紧,需要用滚动数组。

      (反正我只会记忆化搜索~>.^_^.<~)

    代码:

    /*Code by 520 -- 9.10*/
    #include<bits/stdc++.h>
    #define il inline
    #define ll long long
    #define RE register
    #define For(i,a,b) for(RE int (i)=(a);(i)<=(b);(i)++)
    #define Bor(i,a,b) for(RE int (i)=(b);(i)>=(a);(i)--)
    using namespace std;
    typedef pair<int,ll> P;
    ll l,r,L,R;
    int num[21],top;
    map<P,ll>f;
    
    ll dfs(int pos,int limit,ll tot){
        if(!pos){if(tot==-1)tot=0;return L<=tot&&tot<=R;}
        P state=(P){pos,tot};
        if(!limit&&f[state]) return f[state]-1;
        ll tp=0;
        For(i,0,limit?num[pos]:9)
            if(tot==-1) tp+=i?dfs(pos-1,limit&&i==num[pos],i):dfs(pos-1,limit&&i==num[pos],-1);
            else tp+=dfs(pos-1,limit&&i==num[pos],tot*i);
        if(!limit) f[state]=tp+1;
        return tp;
    }
    
    il ll solve(ll x){
        if(x==-1) return 0;
        top=0;
        while(x) num[++top]=x%10,x/=10;
        return dfs(top,1,-1);
    }
    
    int main(){
        cin>>l>>r>>L>>R;
        cout<<solve(r)-solve(l-1);
        return 0;
    }    
  • 相关阅读:
    Linux下删除某些非法字符文件名的文件
    华为面经之我的同学是个死神
    算法面经之华为
    【剑指offer】两个链表的第一个公共结点
    算法面经之百度
    【剑指offer】丑数
    【剑指offer】把数组排成最小的数
    【深度学习最优化方法】
    【矩阵范数与秩、正定】
    算法面经之大华+顺丰+爱奇艺
  • 原文地址:https://www.cnblogs.com/five20/p/9622063.html
Copyright © 2011-2022 走看看