Uva 11361 - Investigating Div-Sum Property(数位DP)

An integer is divisible by 3 if the sum ofits digits is also divisible by 3. For example, 3702 is divisible by 3 and12(3+7+0+2) is also divisible by 3. This property also holds for the integer 9.In this problem, we will investigate this property for other integers.

 

Input

The first line of input is an integer T (T< 100) that indicates the number of test cases. Each case is a linecontaining 3 positive integers A, B and K. 1 ≤ A ≤ B < 2 31 and 0 < K< 10000.

 

Output

For each case, output the number ofintegers in the range [A, B] which is divisible by K and the sum of its digitsis also divisible by K.

 

Sample Input

3

1 20 1

1 20 2

1 1000 4

 

Sample Output

20

5

64

 

【题意】

给定正整数a,b,k,你的任务是在所有满足a<=n<=b中的整数n中,统计有多少个满足n自身是k的倍数,且n的各位数字之和也是k的倍数?

 

【思路】

   这种题的固定套路是设f(x)为[0,x]中满足题意的解的个数,那么本题的答案就是f(b)-f(a-1)。关键问题就是求解f函数。因为数据范围太大,无法穷举,所以这道题要用分段求和的思想来解决问题。比如说要求解f(3212),那么就把[0,3212]拆分出来,也就是

第一部分 0***,1***,2****

第二部分 30**,31**

第三部分 320*

第四部分 3210,3211,3212

(*表示0-9任意一个数)

然后分别求解求和,求解时用到数位dp,设dp(d,m1, m2)表示有d位*,各位数字和%k==m1,整体%k==m2,那么现在考虑它的递推式。假设d位*中的最高位是x,那么现在的数字就是x****…*(d-1个*),设后面d-1个*的各位数字之和为a,整体为b,那么(x+a)%k=m1, (x*10^(d-1)+b)%k=m2

反解出a%k=((m1-x)%k+k)%k, b%k=((m2-10^(d-1)*x)%k+k)%k

所以递推公式就是dp(d,m1, m2)=sum{dp(d-1, a%k, b%k)} (0<=x<=9)

 

对递归结束条件d==0时的解有两种理解方法

<1>当d==1时,即dp(1,m1,m2)将d==1代入可得a%k=((m1-x)%k+k)%k, b%k=((m2-x)%k+k)%k。而对dp(1,m1,m2)来说,只有一个*,位数和和自身相等,如果m1!=m2,那么dp(1,m1,m2)=0,如果m1==m2,那么当x%k==m1时即x=ck+m1即(m1-x)%k=0即a%k=0,b%k=0时有一组对应解。所以dp(0,m1,m2)==1(m1=0, m2=0),其他情况dp(0,m1, m2)==0。

 

<2>其实直观的去想,当d==0时一个*也没有,表示的就是数字0,而0对任何数求余还是0,所以当m1==m2==0的时候,0符合条件dp(0,m1,m2)==1,否则为0。


还有一个细节要注意就是虽然k的范围是[1,10000]但是由于a,b都在int范围内,所以位数和最大不会超过82,当k>82以后,无论如何一个int整数的各位数字之和 mod k != 0,也就是无解,直接输出0即可。 

最后就是f(x)的计算了,把x的每一位都存储起来,低位在前。通过模拟的方式,用bitsum记录之前已经积累的位数和,sum记录之前已经累计的整体和,具体的细节还要在代码中体现。

#include<bits/stdc++.h>
using namespace std;

const int pw[15] = { 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000 };

int a, b, k;
int dp[15][100][100];
int bit[15];

int dfs(int d, int m1, int m2) {
	if (0 == d)	return dp[d][m1][m2] = (m1 == 0 && m2 == 0) ? 1 : 0;//关键! 
	if (-1 != dp[d][m1][m2]) return dp[d][m1][m2];
	
	int ans = 0;
	for (int x = 0; x <= 9; x++) {
		ans += dfs(d - 1, ((m1 - x) % k + k) % k, ((m2 - x*pw[d-1]) % k + k) % k);
	}
	return dp[d][m1][m2] = ans;
}

int f(int x) {//求出[0,x]中符合题意的数的个数 	

	if (0 == x) return 1;//特例,0对任何数求余都是0 
	
	//按位存储数字 
	int cpy = x, size = 0;
	memset(bit, 0, sizeof(bit));
	while (cpy) {
		bit[size++] = cpy % 10;
		cpy /= 10;
	}
	
	int ans = 0;
	int bitsum = 0;//位数和 
	int sum = 0;//整体和 
	
	for (int i = size - 1; i >= 0; i--) {
		if (i) {
	 		for (int j = 0; j < bit[i]; j++) {
				ans += dfs(i, (k-(bitsum+j)%k )%k, (k- (sum+j*pw[i])%k )%k );
				//ans += dfs(i, ((-(bitsum+j))%k + k)%k  ,  ((-(sum+j*pw[i]))%k + k)%k  );
				//两种写法,同模的式子正确即可 
			}
		}
		else {
			for (int j = 0; j <= bit[i]; j++) {//个位,可以取到最大值 
				ans += dfs(i, (k-(bitsum+j)%k )%k, (k- (sum+j*pw[i])%k )%k );
			}
		}
		bitsum += bit[i];//及时更新 
		sum += bit[i]*pw[i];
	}
	
	return ans;
}

int main() {
	int t;
	scanf("%d", &t);
	while (t--) {
		scanf("%d%d%d", &a, &b, &k);
		if (k > 90) { printf("0\n"); }
		else {
			memset(dp, -1, sizeof(dp));
			int ans = f(b) - f(a - 1);
			printf("%d\n", ans);
		}
	}
	return 0;
}


posted @ 2017-12-23 10:33  不想吃WA的咸鱼  阅读(130)  评论(0编辑  收藏  举报