数字和与倍数 UVA 11361

这一道题目弄了两天,终于对数位dp有了新的理解.

参考了一个大佬的代码大佬的博客
题目大意:
给定,,a,b,k.找出所有满足在a<=x<=b条件的整数,要求能被k整除,数位之和也能被k整除。
分析
递推题目的第一步大致相同,设f(x)表示不超过x的非负整数中满足条件的个数。那么我们所求答案便是f(b)-f(a-1);问题的关键就在于如何计算f函数.假设我们需要计算f(12345)就是要数一数在0~12345范围内有多少满足要求的数,由于题目中给的范围很大,一一枚举是不可能的,我们得想办法"取巧",白书上介绍的方法是使用加法原理,分段求和。

每一段都可以使用一个包含若干固定前缀和用星号表示的"后缀"的模板。其中星号表示可以人选用一个数字。
设f(d,m1,m2)表示 共d个数字,其中各个数字之和除以k的余数为m1,这些数字组成的整数除以k的余数为m2;

递推公式
f(d,m1,m2)={sum(f(d-1,(m1-x)mod k,(m2-10d-1)mod k|x=0,1,2,……9};

#include<iostream>
#include<algorithm>
#include<string.h>
#define maxn 105
using namespace std;

typedef long long ll;
ll  num[20];
int di[15], a, b, k, n;
int d[15][maxn][maxn], vis[15][maxn][maxn];


void pow_10() {
	num[0]=1;
	for (int i = 1; i <= 15; i++)
		num[i]=num[i-1]*10;
}
int dp(int cur, int m1, int m2, bool judge) {
	int &ans = d[cur][m1][m2];
	if (vis[cur][m1][m2]&&!judge)  return ans;    //记忆搜索如果是上届不可以按照平常方式一样返回    
	int ans0=0;                                    //备用
	vis[cur][m1][m2] = 1;
	if (cur == 0) {                   //出口
		if (m1 == 0 && m2 == 0)   
			return ans=1;
		return ans=0;
	}
	for (int i = 0; i < (judge ? di[cur] : 10); i++) {     //当上一位触及到上界时,该位置也必须进行约束
	 ans0+= dp(cur - 1, (m1 + i) % k, (m2 + i * num[cur - 1]) % k, false);
	}
	if (judge)    //触及上界,单独处理
		ans0+= dp(cur - 1, (m1 + di[cur]) % k, (m2 + di[cur] * num[cur - 1]) % k, true);
	if (!judge)  ans = ans0;     //如果是上界因为单独处理,需要判断
	return ans0;
}
int solve(int nu) {
	if (nu == 0)  return 1;
	n = 0;
	while (nu) {
		di[++n] = nu % 10;
		nu /= 10;
	}
	return dp(n, 0, 0, true);
}

int main() {
	int t;
	cin >> t;
	while (t--) {
		memset(d, 0, sizeof(d));
		memset(vis, 0, sizeof(vis));
		cin >> a >> b >> k;
		pow_10();
		if (k > 100) { cout << 0 << '\n'; continue; }
		int bb = solve(b);
		int aa = solve(a-1);
		cout << solve(b) - solve(a - 1) << '\n';
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值