Link:
Solution:
一句话的题目,看得爽,做得烦
一般这类和数位相关的都是数位$dp$吧
不过一开始还是感觉不太可做,毕竟每个数模数不同
但要发现,模数最高也只可能为$9*19=171$,
于是只要将数按照他们的数位和(即模数)分类计算即可
这样便暴力解决了模数不同的问题
设$dp[sp][sum][rmd][lmt]$表示:
枚举到第$sp$高位,剩下的数的和位$sum$,此时对$mod$余$rmd$时的方案数(lmt表示是否达到上界)
感觉数位$dp$还是用记忆化搜索写起来逻辑比较清晰吧
Code:
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int MAXN=175; //n可能为19,sum最大为171 int vis[20][MAXN][MAXN][2],mod,dgt,cur,num[20]; ll a,b,dp[20][MAXN][MAXN][2]; ll dfs(int sp,int sum,int rmd,int lmt) { if(!sp) return !sum && !rmd; if(vis[sp][sum][rmd][lmt]==cur) return dp[sp][sum][rmd][lmt]; vis[sp][sum][rmd][lmt]=cur;ll ret=0; int l=max(0,sum-(sp-1)*9),r=min((lmt)?num[sp]:9,sum); for(int i=l;i<=r;i++) ret+=dfs(sp-1,sum-i,(rmd*10+i)%mod,lmt&(i==num[sp])); return dp[sp][sum][rmd][lmt]=ret; } ll solve(ll x) { ll ret=0; for(dgt=0;x;x/=10) num[++dgt]=x%10; for(mod=1;mod<=dgt*9;mod++) cur++,ret+=dfs(dgt,mod,0,1); return ret; } int main() { scanf("%lld%lld",&a,&b); printf("%lld ",solve(b)-solve(a-1)); return 0; }
Review:
1、少用$memset$,尽量在使用时顺便初始化
为了区分不同次的调用,可以在每一次调用打上不同的标记
2、在$dp$时难以处理模数不同的情况
考虑将数据分类后直接暴力所有可能的模数
3、$1e18$可能有19位,$MAXN$要设为175
(一开始扫了一眼题解上$MAXN$为165,以后还是要自己算啊……)