数字整除
题目链接:ybt高效进阶5-3-4
题目大意
问你 l~r 的数中有多少个数满足它能被它各位数字之和整除。
思路
看到这种题就知道是数位 DP。
首先老套路, [ l ∼ r ] → [ 1 ∼ r ] − [ 1 ∼ l − 1 ] [l\sim r]\rightarrow[1\sim r]-[1\sim l-1] [l∼r]→[1∼r]−[1∼l−1],然后接着看
数位 DP 最主要的就是从高位向地位搞,然后不压着后面的就随便选,压着后面就要继续枚举下去看。
而要 DP 求出的就是后面随便选的方案数。
那由于它有各位数字之和和对某个模数取模的余数,所以我们设
f
i
,
j
,
k
,
l
f_{i,j,k,l}
fi,j,k,l 位前
i
i
i 位,取模的余数是
j
j
j,当前各个数字之和是
k
k
k,取模的数是
l
l
l 的方案数。
然后就枚举每个数,转移过去。
然后记得后面随便选的时候,假设前面绑好的余数是 x x x,数字之和是 y y y,你要的数字之和(取模的数)是 z z z,那你要的 f f f 里的余数应该是 ( z − x ) m o d x (z-x)\bmod x (z−x)modx, f f f 里的数字之和应该是 z − y z-y z−y。
然后不难看到你有两个不确定量,所以要枚举模数,对于每个模数都搞一次。
然后就好啦~
代码
#include<cstdio>
using namespace std;
int l, r, f[11][101][101][101];
int ten[11], a[11], an;
void DP() {//DP 出随便填数字的种数
for (int i = 1; i <= 100; i++)
f[0][0][0][i] = 1;
for (int mod = 1; mod <= 100; mod++)
for (int i = 1; i <= 10; i++)
for (int num1 = 0; num1 <= mod; num1++)
for (int else1 = 0; else1 < mod; else1++) {
if (!f[i - 1][else1][num1][mod]) continue;
for (int j = 0; j <= 9; j++) {
int num2 = num1 + j;
int else2 = (else1 + ten[i - 1] % mod * j % mod) % mod;
f[i][else2][num2][mod] += f[i - 1][else1][num1][mod];
}
}
}
int work(int x, int mod) {
int re = 0, else1 = 0, num1 = 0;
an = 0;
while (x) {
a[++an] = x % 10;
x /= 10;
}
for (int i = an; i >= 1; i--) {
for (int j = 0; j < a[i]; j++) {//没填满,后面随便
int else2 = (mod - (else1 + ten[i - 1] % mod * j) % mod) % mod;
int num2 = mod - (num1 + j);
if (num2 < 0) break;
re += f[i - 1][else2][num2][mod];
}
else1 = (else1 + ten[i - 1] % mod * a[i]) % mod;
num1 += a[i];//填满,看下一位
}
if (num1 == mod && !else1) re++;//最后这个数记得算上
return re;
}
int main() {
ten[0] = 1;
for (int i = 1; i <= 10; i++) ten[i] = ten[i - 1] * 10;
DP();
while (scanf("%d %d", &l, &r) != EOF) {
int re = 0;
for (int i = 1; i <= 100; i++) {
re += work(r, i) - work(l - 1, i);
}
printf("%d\n", re);
}
return 0;
}