bzoj1799 self 同类分布(数位dp)

题意:给出a,b,求出[a,b]中各位数字之和能整除原数的数的个数。

分析:一个数的数字和是很小的,最大为9*18,那么我们可以枚举这个数位和,因为这个数位和是要作模数的,只能枚举,所以dp的时候必须要加上这一维度才好递推。f[i][j][k][0/1]表示到第i位数位和为j,在模sum意义下的余数为k,是否卡上界的数的个数。f[0][i][j][k]表示前i位全都和原数x相等的数的个数(卡上界),f[1][i][j][k]表示前i位至少有一位小于x相应数位的数的个数(不卡上界)。

代码:

#include <cstring>
#include <iostream>
#define ll long long
using namespace std;
const int N = 200, L = 21;
ll f[L][N][N][2];
int n[L];

ll calc(ll x, int P) {
	if (!x) return 0;
	memset(f, 0, sizeof(f));
	int t = 0;
	while (x) {
		n[++t] = x % 10;
		x /= 10;
	}
	f[t+1][0][0][0] = 1;
	for (int i = t + 1; i > 1; i--)
		for (int j = 0; j <= P; j++)
			for (int k = 0; k < P; k++)
				if (f[i][j][k][0] || f[i][j][k][1])///剪枝
					for (int p = 0; p < 10; p++) {
						int w = (10 * k + p) % P;
						if (p < n[i-1] && j + p <= P) ///计算不卡上界的贡献
							f[i-1][j+p][w][1] += f[i][j][k][0];
						else if (p == n[i-1] && j + p <= P) ///计算卡上界的贡献
							f[i-1][j+p][w][0] += f[i][j][k][0];
						if (f[i][j][k][1] && j + p <= P) ///如果不卡上界 计算p>n[i-1]情况下的贡献
							f[i-1][j+p][w][1] += f[i][j][k][1];
					}
	return f[1][P][0][0] + f[1][P][0][1];
}

int main() {
	ll a, b, ans = 0;
	cin >> a >> b;
	for (int i = 1; i <= 9 * 18; i++)///枚举数位和i
		ans += calc(b, i) - calc(a - 1, i);
	cout << ans << endl;
	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值