【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

 

posted @ 2017-08-21 22:28  ONION_CYC  阅读(202)  评论(0编辑  收藏  举报