• 数位dp CodeForces


    Beautiful numbers CodeForces - 55D 

    题意:定义能被自己所有位数整除的数字为美丽,给定一个区间,求区间内的美丽数字个数。

    分析:首先,可以把限制条件转化为之前所有位数的最大公倍数,将pos,sum,lcm,up当作      dfs的条件和dp下标,然后 dp[ pos ][ sum ][ lca ][ up ] 就代表着 pos 之后的位置全部遍历完后,该 状态取这个sum的最大值。这里要避免一个问题,就是 15 和 12,不可以从 15 的 dp[ pos=1 ][ sum=0 ][ up=1 ] 的记忆直接得到 12 的 dp[ pos=1 ][ sum=0 ][ up=1 ] ,而数位dp又需要这样的记忆化来减少时间复杂度,因此,这里的 up 就有了两个作用,即判断某个位置可以遍历到数字几 和 将15 的dp[ 1 ] 定义为饱和(避开15的dp[1] 和 12的dp[1],而选择15的dp[0] 来记忆退得 12的dp[0] ),那么利用记忆化的时候使用的是  dp[ 0 ] = 10 , 即up=0的上一位,就完美解决了。 并且这样就可以用memset一次,别的都可以用记忆化,大大减少时间复杂度。

    然后发现MLE了,原因在于数组开太大 ,而1~9的公倍数为2520,[ sum ]的最大值就可以看成 2520,大的就用取模(有相关数论),所以这个只要开2520。并且,[ lca ]位置很多都没用上,1~9的公倍数就那么多少个,所以就要用到离散化,建立一个Hash数组,在操作dp这个数组的时候,把[ lca ]位置的值用Hash数组转化为 cnt 就行了,这个数字大概就50个。

    还可以看到Lcm(a,b)=a/gcd(a,b)*b

    #include<cstdio>
    #include<cstring>
    #include<cmath>
    #include<iostream>
    #include<algorithm>
    using namespace std;
    
    typedef long long ll;
    const int maxn=32;
    const int Mod=2520;
    ll n,m;
    int dig[maxn];
    ll dp[maxn][2550][50][2];
    int Hash[2550];
    
    int gcd(int a,int b){
        if(b==0) return a;
        return gcd(b,a%b);
    }
    int Lcm(int a,int b){
        return a/gcd(a,b)*b;
    }
    
    ll dfs(int pos,int sum,int lcm,bool up) {
        // printf("state = %d %d %d %d 
    ", pos,sum,lcm,up);
        if(pos<0) return sum%lcm==0;
        if(!up&&dp[pos][sum][Hash[lcm]][up]!=-1) {
            printf("dp %d %d %d %d = %d
    ", pos,sum,lcm,up,dp[pos][sum][Hash[lcm]][up]);
            return dp[pos][sum][Hash[lcm]][up];
        }
        ll res=0;
        for(int i=0; i<=9; i++){
            if(up==1 && i>dig[pos]) break;
            int tlcm=lcm;
            if(i) tlcm=Lcm(tlcm,i);
            res += dfs(pos-1,(sum*10+i)%Mod,tlcm,up&&i==dig[pos]);
            // printf("res=%lld
    ",res );
        }
        if(!up) dp[pos][sum][Hash[lcm]][up]=res;
        return res;
    }
    
    ll sol(ll x){
        if(x<0) return 0;
        // memset(dp,-1,sizeof(dp));
        int cnt=0;
        while(x){
            dig[cnt++]=x%10;
            x/=10;
        }
        return dfs(cnt-1,0,1,1);
    }
    
    int main(){
        int T;
        cin>>T;
        int cnt=0;
        for(int i=1; i<=Mod; i++){
            if(Mod%i==0){
                Hash[i]=cnt++;
            }
        }
        // printf("Hash=%d
    ", Hash[126]);
        memset(dp,-1,sizeof(dp));
        while(T--){
            cin>>n>>m;
            cout<<sol(m)-sol(n-1)<<endl;
        }
    }
  • 相关阅读:
    存储结构接收数组
    oracle数据库sql根据查看执行计划优化sql--走不走索引
    多线程--Thread
    java常用集合族谱
    设计模式之二 适配模式
    Tomcat优化问题
    设计模式之一
    C++虚函数表,虚表指针,内存分布
    设计模式
    linux环境下的时间编程
  • 原文地址:https://www.cnblogs.com/-Zzz-/p/11415938.html
Copyright © 2020-2023  润新知