Count of numbers between range having only non-zero digits whose sum of digits is N and number is divisible by M

Given a range [L, R] and two positive integers N and M. The task is to count the numbers in the range containing only non-zero digits whose sum of digits is equal to N and the number is divisible by M.

Examples:

Input: L = 1, R = 100, N = 8, M = 2
Output: 4
Only 8, 26, 44 and 62 are valid numbers

Input: L = 1, R = 200, N = 4, M = 11
Output: 2
Only 22 and 121 are valid numbers

Prerequisites : Digit DP

Approach: Firstly, if we are able to count the required numbers up to R i.e. in the range [0, R], we can easily reach our answer in the range [L, R] by solving for from zero to R and then subtracting the answer we get after solving for from zero to L – 1. Now, we need to define the DP states.
DP States:

  • Since we can consider our number as a sequence of digits, one state is the position at which we are currently in. This position can have values from 0 to 18 if we are dealing with the numbers up to 1018. In each recursive call, we try to build the sequence from left to right by placing a digit from 0 to 9.
  • Second state is the sum of the digits we have placed so far.
  • Third state is the remainder which defines the modulus of the number we have made so far modulo M.
  • Another state is the boolean variable tight which tells the number we are trying to build has already become smaller than R so that in the upcoming recursive calls we can place any digit from 0 to 9. If the number has not become smaller, the maximum limit of digit we can place is digit at the current position in R.

For the number to have only non-zero digits, we maintain a variable nonz whose value if 1 tells the first digit in the number we have placed is a non-zero digit and thus, now we can’t place any zero digit in upcoming calls. Otherwise, we can place a zero digit as a leading zero so as to make number of digits in current number smaller than number of digits in upper limit.

Below is the implementation of the above approach:

filter_none

edit
close

play_arrow

link
brightness_4
code

// C++ implementation of the approach
#include <bits/stdc++.h>
using namespace std;
  
const int M = 20;
  
// states - position, sum, rem, tight
// sum can have values upto 162, if we
// are dealing with numbers upto 10^18
// when all 18 digits are 9, then sum
// is 18 * 9 = 162
int dp[M][165][M][2];
  
// n is the sum of digits and number should
// be divisible by m
int n, m;
  
// Function to return the count of
// required numbers from 0 to num
int count(int pos, int sum, int rem, int tight,
          int nonz, vector<int> num)
{
    // Last position
    if (pos == num.size()) {
        if (rem == 0 && sum == n)
            return 1;
        return 0;
    }
  
    // If this result is already computed
    // simply return it
    if (dp[pos][sum][rem][tight] != -1)
        return dp[pos][sum][rem][tight];
  
    int ans = 0;
  
    // Maximum limit upto which we can place
    // digit. If tight is 1, means number has
    // already become smaller so we can place
    // any digit, otherwise num[pos]
    int limit = (tight ? 9 : num[pos]);
  
    for (int d = 0; d <= limit; d++) {
  
        // If the current digit is zero
        // and nonz is 1, we can't place it
        if (d == 0 && nonz)
            continue;
        int currSum = sum + d;
        int currRem = (rem * 10 + d) % m;
        int currF = tight || (d < num[pos]);
        ans += count(pos + 1, currSum, currRem,
                     currF, nonz || d, num);
    }
    return dp[pos][sum][rem][tight] = ans;
}
  
// Function to convert x into its digit vector
// and uses count() function to return the
// required count
int solve(int x)
{
    vector<int> num;
    while (x) {
        num.push_back(x % 10);
        x /= 10;
    }
    reverse(num.begin(), num.end());
  
    // Initialize dp
    memset(dp, -1, sizeof(dp));
    return count(0, 0, 0, 0, 0, num);
}
  
// Driver code
int main()
{
    int L = 1, R = 100;
    n = 8, m = 2;
    cout << solve(R) - solve(L);
    return 0;
}

chevron_right


Output:

4


My Personal Notes arrow_drop_up