【ybt高效进阶5-3-4】数字整除(数位DP)

数字整除

题目链接: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] [lr][1r][1l1],然后接着看

数位 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 (zx)modx f f f 里的数字之和应该是 z − y z-y zy

然后不难看到你有两个不确定量,所以要枚举模数,对于每个模数都搞一次。

然后就好啦~

代码

#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;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值