【AHOI2009】同类分布 题解(数位DP)
题目大意:求$[l,r]$中各位数之和能被该数整除的数的个数。$0\leq l\leq r\leq 10^{18}$。
------------------------
显然数位DP。
搜索时记录$pos$表示当前位置,$sum$表示各位数字之和,$st$表示原数,$limit$表示最高位限制。(如果有时间我会写一篇博客学习数位DP,希望不要咕咕
转移自然是$dfs(pos+1,sum+i,st*10+i)$
但是一看数据范围,肯定不能这么搜,不然内存会炸。这时我们不妨考虑取模。
根据题目,我们发现:当$st\mod\ 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; }