zoukankan      html  css  js  c++  java
  • 【BZOJ】1799: [Ahoi2009]self 同类分布

    【题意】给出a,b,求出[a,b]中各位数字之和能整除原数的数的个数。1 ≤ a ≤ b ≤ 10^18

    【算法】数位DP

    【题解】

    感觉这种方法很暴力啊。

    枚举数位和1~162(不能枚举0,不然会模0,相当于除0),记忆化f[pos][sum][val],sum表示当前数位和,val表示数字取模枚举的数位和。

    每次sum+i和(val*10+i)%MOD转移。

    sum用减法优化,即记忆化(MOD-sum),但是枚举过程中都要memset,导致效率低下,记忆化效果很差。

    要什么方法才能跑1.3s啊,%%%。

    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #define ll long long
    using namespace std;
    const int maxn=20;
    ll f[maxn][200][200],a[maxn],b[maxn],c[maxn],n,MOD;
    ll dfs(int pos,int sum,int val,int limit){
        if(sum>MOD)return 0;
        if(pos==-1){if(sum==MOD&&val==0)return 1;else return 0;}
        if(!limit&&~f[pos][MOD-sum][val])return f[pos][MOD-sum][val];
    
        int up=limit?a[pos]:9;
        ll ans=0;
        for(int i=0;i<=up;i++){
            ans+=dfs(pos-1,sum+i,(val*10+i)%MOD,limit&&i==up);
        }
        if(!limit)f[pos][MOD-sum][val]=ans;
        return ans;
    }
    int main(){
        ll A,B,cntb=0,cntc=0;
        scanf("%lld%lld",&A,&B);
        A--;
        while(A){
            b[cntb++]=A%10;
            A/=10;
        }
        while(B){
            c[cntc++]=B%10;
            B/=10;
        }
        ll ans=0;
        for(int i=1;i<=162;i++){//不能模0啊!!!模0也是除0啊!!! 
            memset(f,-1,sizeof(f));
            MOD=i;
            n=cntb;
            for(int j=0;j<n;j++)a[j]=b[j];
            ans-=dfs(n-1,0,0,1);
            n=cntc;
            for(int j=0;j<n;j++)a[j]=c[j];
            ans+=dfs(n-1,0,0,1);
        }
        printf("%lld",ans);
        return 0;
    }
    View Code
  • 相关阅读:
    tarjan algorithm
    最小生成树
    Manacher's Algorithm (马拉车算法)
    KMP
    Trie(字典树)
    Hash
    GDB调试
    图论
    扫描线
    STL
  • 原文地址:https://www.cnblogs.com/onioncyc/p/7407056.html
Copyright © 2011-2022 走看看