题目链接:HDOJ3967
题目:把【A,B】区间中的每个数X,分成左右两部分,如果和是K的倍数,那么算一个满足条件的数。求区间内的总数
分成左右两部分意思是说从任意一个数位断开,比如1234,可以分成1+234,也可以是12+34,还可以是123+4
如果有多种分解情况,算多种
然后就跟平衡数的思路一样了
平衡数是枚举支点,这个题就是枚举分隔的位置
如果X这个数有len位,那么枚举len-1个分界点,有一个符合就算一个
记忆化的写法就好,还是需要主要好10的次幂的打表与当前数位的位置要对应好
纠正以前理解数位DP中前导0的理解错误
在记忆化使用的时候可以不需要zero判断
但是在记录的时候需要
反应在代码中就是IF语句中的判断
#include<map>
#include<set>
#include<math.h>
#include<time.h>
#include<iostream>
#include<cstdio>
#include<queue>
#include<stack>
#include<stdio.h>
#include<cstring>
#include<string.h>
#include<algorithm>
#include<cstdlib>
using namespace std;
#define lson rt<<1,l,mid
#define rson rt<<1|1,mid+1,r
#define ll rt<<1
#define rr rt<<1|1
#define LL long long
#define ULL unsigned long long
#define maxn 1050
#define maxnum 1000050
#define eps 1e-6
#define input freopen("input.txt","r",stdin)
#define output freopen("output.txt","w",stdout)
#define MOD 1000000007
LL dp[20][50];
LL p[20];
int digit[20];
int K,sep;
LL dfs(int len,int divide,bool zero,bool flag){
if (len==0) return divide==0;
if (flag&&dp[len][divide]!=-1) return dp[len][divide];
int up=flag?9:digit[len];
LL ret=0;
for(int i=0;i<=up;i++){
if (zero==0&&i==0&&len==sep+1) continue;
ret+=dfs(len-1,(divide+(len>sep?i*p[len-sep-1]:i*p[len-1]))%K,zero||i,flag||i<up);
}
if (flag&&zero) dp[len][divide]=ret;
return ret;
}
LL calc(LL x){
if (x<=0) return 0;
int len=0;
while(x){
digit[++len]=x%10;
x/=10;
}
LL ret=0;
for(int i=len-1;i>=1;i--){
sep=i;
memset(dp,-1,sizeof(dp));
ret+=dfs(len,0,0,0);
}
return ret;
}
int main(){
//input;
p[0] = 1;
for(int i = 1; i < 20; ++i)
p[i] = p[i-1]*10;
LL A, B;
while(~scanf("%I64d%I64d%d", &A, &B, &K))
printf("%I64d\n", calc(B)-calc(A-1));
return 0;
}