【AHOI2009】同类分布 题解(数位DP)

题目大意:求$[l,r]$中各位数之和能被该数整除的数的个数。$0leq lleq rleq 10^{18}$。

------------------------

显然数位DP。

搜索时记录$pos$表示当前位置,$sum$表示各位数字之和,$st$表示原数,$limit$表示最高位限制。(如果有时间我会写一篇博客学习数位DP,希望不要咕咕

转移自然是$dfs(pos+1,sum+i,st*10+i)$

但是一看数据范围,肯定不能这么搜,不然内存会炸。这时我们不妨考虑取模

根据题目,我们发现:当$stmod sum==0$的时候,该数是合法的。所以我们不妨枚举$mod$,当$st==0且sum==mod$时返回$1$。一共$18$位数,共$162$种状态。

代码:

#include<bits/stdc++.h>
#define int long long
using namespace std;
int l,r,len,mod;
int a[20],dp[20][200][200];
inline int dfs(int pos,int sum,int st,int limit)//pos位置,sum各位数字和,st原数,limit最高位限制 
{
    if (pos>len&&sum==0) return 0;
    if (pos>len) return st==0&&sum==mod?1:0;
    if (!limit&&dp[pos][sum][st]!=-1) return dp[pos][sum][st];
    int ret=0,res=limit?a[len-pos+1]:9;
    for (int i=0;i<=res;i++)
        ret+=dfs(pos+1,sum+i,(10ll*st+i)%mod,i==res&&limit);
    return limit?ret:dp[pos][sum][st]=ret;
}
inline int chai(int x)
{
    len=0;
    while(x>0){
        a[++len]=x%10;
        x/=10;
    }
    int res=0;
    for (mod=1;mod<=9*len;mod++)
    {
        memset(dp,-1,sizeof(dp));
        res+=dfs(1,0,0,1);
    }
    return res;
}
signed main()
{
    scanf("%lld%lld",&l,&r);
    printf("%lld",chai(r)-chai(l-1));
    return 0;
}
原文地址:https://www.cnblogs.com/Invictus-Ocean/p/13273635.html