状态设计:
- f[当前位数][之前每一位的和][当前余数][当前位数字是否与原数相同].
- 转移方式是通过余数的结构持久保存和对初始状态的特殊判定实现的.
- 在代码中之所以需要分开进行转移,是由于需要在保证当前数字在范围内的同时又需要对一些高位小但其他位大的数字进行转移。为了兼顾两种情况,将初始状态设为需要判定才能转移即可.
易错点:
- 需要对取模运算的深刻认识.
- 需要对数位DP转移方式的深刻认识.
一句话总结:
- 完美的线性递推结构与合理的状态设计思想.
#include<cstdio>
#include<iostream>
#include<cstring>
#define ll long long
using namespace std;
ll f[25][190][190][2],dight[25],dightTop=0;
ll calc(ll num,int mod){
memset(f,0,sizeof(f));
dightTop=0;
while(num){
dight[++dightTop]=num%10;
num/=10;
}
f[dightTop+1][0][0][1]=1;
for(int i=dightTop+1;i>1;i--){
for(int j=0;j<=mod;j++){
for(int k=0;k<mod;k++){
if(!f[i][j][k][0]&&!f[i][j][k][1])continue;
for(int p=0;p<=9;p++){
f[i-1][j+p][(k*10+p)%mod][0]+=f[i][j][k][0];
if(p==dight[i-1])f[i-1][j+p][(k*10+p)%mod][1]+=f[i][j][k][1];
else if(p<dight[i-1])f[i-1][j+p][(k*10+p)%mod][0]+=f[i][j][k][1];
}
}
}
}
return f[1][mod][0][0]+f[1][mod][0][1];
}
int main(){
ll l,r;
scanf("%lld%lld",&l,&r);
ll ans=0,dightNumber=9*21;
for(int i=1;i<dightNumber;i++){
ans+=calc(r,i)-calc(l-1,i);
}
printf("%lld
",ans);
return 0;
}