ACM: uva 11361 -&…

Investigating Div-Sum Property

An integer is divisible by 3 if the sum of its digits is also divisible by 3. For example, 3702 is divisible by 3 and 12(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 line containing 3 positive integers AB and K. 1 <= A <= B < 2^31 and 0<<B>K<10000.

Output

For each case, output the number of integers in the range [AB] which is divisible by K and the sum of its digits is also divisible by K.

 

Sample Input

3

1 20 1

1 20 2

1 1000 4

 

Output for Sample Input

20

5

64

 

题意: 给出一个范围[a, b];, 判断这个范围内的数有多少个满足要求, 要求: 数能整除K, 每位数相加和

      能整除K.

 

解题思路: (一道好题)

     1. 给定一个范围求里面满足条件的数, 暴力枚举一般不可行. 设f(x)表示不超过x的非负整数中满足

        条件的个数. 显然结果变成f(b)-f(a-1);

     2. 问题变成怎么计算f(n)函数了, 可以用加法原理, 每一个数字段用一个固定前缀和不确定后缀表示.

        例如: 123***, 123前缀是固定的, ***是可以任意的3个数字. 表示123000~123999.

     3. 多阶段问题, 用记忆化搜索思路, 简化编程复杂性.

        设状态dp[d][m1][m2]表示d个数字, m1表示个数字的和对K求余的数, m2表示整个数字对K求余的数.

        状态方程: dp[d][m1][m2] = sum(dp[d-1][ (m1+x)%k ][ (m2+x*10^(d-1))%k ])

                  (x = 0, 1, 2, 3, ... , 9);

        边界条件: if( d == 0 && (m1+x)%k == 0 && (m2+x*10^(d-1))%k ) return 1;

        即: 当全部位数确定之后, 已经满足条件, 这个数字就是需要的数字, 结果+1;

 

代码:

#include <cstdio>
#include <iostream>
#include <cstring>
using namespace std;
#define MAX 105
typedef long long ll;

int a, b, k;
int dp[15][MAX][MAX], num[15];
ll pow[15];

void init()
{
 ll x = 1;
 for(int i = 0; i <= 10; ++i)
 {
  pow[i] = x;
  x *= 10;
 }
}

int DP(int d, int m1, int m2, int flag) //flag: 标记最高位 和区分状态
{
 if( d == 0 ) return (m1 == 0 && m2 == 0);
 if( !flag && dp[d][m1][m2] != -1) return dp[d][m1][m2];

 int size = (flag ? num[d] : 9);
 int ans = 0;
 for(int x = 0; x <= size; ++x)
  ans += DP(d-1, (m1+x)%k, (m2+pow[d-1]*x)%k, flag && x == size);

 return flag ? ans : dp[d][m1][m2] = ans;
}

int solve(int a)
{
 int len = 0;
 while( a )
 {
  num[++len] = a;
  a /= 10;
 }

 memset(dp, -1, sizeof(dp));
 return DP(len, 0, 0, 1);
}

int main()
{
// freopen("input.txt", "r", stdin);
 init();
 int caseNum;
 scanf("%d", &caseNum);
 while(caseNum--)
 {
  scanf("%d %d %d", &a, &b, &k);
  if(k >= 100) printf("0\n");
  else printf("%d\n", solve(b) - solve(a-1));
 }

 return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值