题意
求区间[A, B]内能被K整除且其各位数之和也能被K整除的数。
思路
就是因为当初见了这道题不会才驱使我去学数位DP的,当时真的是一点儿思路都没有,然后看《训练指南》上的递推方法也云里雾里。现在会了数位DP后再看真是一眼题……
设计一下按位记忆化搜索状态就好了:pos表示当前处理第几位,sum_mod表示数本身除K的余数,dig_sum表示各位数字和除K的余数。然后按位DP就好了~~~
终于能发现记忆化搜索式的数位DP的好处了吧!!非常容易理解,也非常容易设计出状态~
代码
[cpp]
#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <string>
#include <cstring>
#include <vector>
#include <set>
#include <stack>
#include <queue>
#define MID(x,y) ((x+y)/2)
#define MEM(a,b) memset(a,b,sizeof(a))
#define REP(i, begin, m) for (int i = begin; i < begin+m; i ++)
using namespace std;
typedef long long LL;
typedef vector <int> VI;
typedef set <int> SETI;
typedef queue <int> QI;
typedef stack <int> SI;
const int oo = 0x7fffffff;
VI num;
int k;
int dp[15][100][100];
int dfs(int pos, int sum_mod, int dig_mod, bool limit){
if (pos == -1) return (sum_mod == 0 && dig_mod == 0);
if (!limit && ~dp[pos][sum_mod][dig_mod]) return dp[pos][sum_mod][dig_mod];
int end = limit?num[pos]:9;
int res = 0;
for (int i = 0; i <= end; i ++){
res += dfs(pos-1, (sum_mod*10+i)%k, (dig_mod+i)%k, limit && (i==end));
}
if (!limit) dp[pos][sum_mod][dig_mod] = res;
return res;
}
int cal(int x){
num.clear();
while(x){
num.push_back(x%10);
x /= 10;
}
MEM(dp, -1);
int tmp = num.size()-1;
return dfs(tmp, 0, 0, 1);
}
int main(){
//freopen("test.in", "r", stdin);
//freopen("test.out", "w", stdout);
int t;
scanf("%d", &t);
while(t --){
int a, b;
scanf("%d %d %d", &a, &b, &k);
if (k > 100){
printf("0
");
}
else{
printf("%d
", cal(b) - cal(a-1));
}
}
return 0;
}
[/cpp]