Digit DP: From Fundamentals to Implementation

Digit dynamic programming (digit DP) is a technique used to count numbers within a range ([l, r]) that satisfy specific digit-based constraints. The standard approach involves transforming the problem into computing (F(r) - F(l-1)), where (F(x)) counts valid numbers from 0 to (x).

The solution typically uses memoized recursion over the digits of the number, tracking constraints such as tight bounds (whether the prefix matches the upper limit) and leading zeros.

Example 1: Windy Numbers

A Windy number has adjacent digits differing by atleast 2. To count such numbers in ([l, r]), compute (F(r) - F(l-1)).

The recursive function processes each digit position, ensuring the absolute difference condition holds unless leading zeros are still being skipped.

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

template <typename T>
void read(T& t) {
    t = 0; char ch = getchar(); int sign = 1;
    while (ch < '0' || ch > '9') { if (ch == '-') sign = -1; ch = getchar(); }
    while (ch >= '0' && ch <= '9') { t = t * 10 + (ch - '0'); ch = getchar(); }
    t *= sign;
}

const int MAX_DIGITS = 20;
int dp[MAX_DIGITS][10];
int digits[MAX_DIGITS], len;

int solve(int pos, int last, bool tight, bool started) {
    if (pos == len) return started ? 1 : 0;
    if (!tight && started && dp[pos][last] != -1) return dp[pos][last];
    
    int limit = tight ? digits[pos] : 9;
    int total = 0;
    for (int d = 0; d <= limit; ++d) {
        bool new_started = started || (d != 0);
        bool valid = !new_started || abs(d - last) >= 2;
        if (valid) {
            total += solve(pos + 1, d, tight && (d == limit), new_started);
        }
    }
    
    if (!tight && started) dp[pos][last] = total;
    return total;
}

int countWindy(int n) {
    if (n < 0) return 0;
    len = 0;
    if (n == 0) digits[len++] = 0;
    else {
        while (n) {
            digits[len++] = n % 10;
            n /= 10;
        }
        reverse(digits, digits + len);
    }
    memset(dp, -1, sizeof(dp));
    return solve(0, 0, true, false);
}

int main() {
    int L, R;
    read(L); read(R);
    cout << countWindy(R) - countWindy(L - 1) << endl;
    return 0;
}

Example 2: Product of Counts of Set Bits

Given (n), compute the product over all integers from 1 to (n) of the count of 1s in their binary representation, modulo (10^7 + 7).

Since the number of set bits is small (at most ~60 for 64-bit numbers), iterate over possible bit counts (k). For each (k), count how many numbers in ([1, n]) have exactly (k) ones using digit DP on binary digits. Multiply (k^{\text{count}}) into the result.

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

typedef long long ll;
const ll MOD = 10000007;

ll modPow(ll base, ll exp) {
    ll res = 1;
    while (exp) {
        if (exp & 1) res = (res * base) % MOD;
        base = (base * base) % MOD;
        exp >>= 1;
    }
    return res;
}

ll dp[65][65];
int bin[65], len;

ll dfs(int pos, int ones, bool tight) {
    if (ones > len) return 0;
    if (pos == len) return (ones == 0) ? 1 : 0;
    if (!tight && dp[pos][ones] != -1) return dp[pos][ones];
    
    int limit = tight ? bin[pos] : 1;
    ll total = 0;
    for (int b = 0; b <= limit; ++b) {
        total += dfs(pos + 1, ones - b, tight && (b == limit));
    }
    
    if (!tight) dp[pos][ones] = total;
    return total;
}

ll solve(ll n) {
    if (n <= 0) return 1;
    len = 0;
    while (n) {
        bin[len++] = n & 1;
        n >>= 1;
    }
    reverse(bin, bin + len);
    
    ll result = 1;
    for (int k = 1; k <= len; ++k) {
        memset(dp, -1, sizeof(dp));
        ll cnt = dfs(0, k, true);
        result = (result * modPow(k, cnt)) % MOD;
    }
    return result;
}

int main() {
    ll n;
    cin >> n;
    cout << solve(n) << endl;
    return 0;
}

Tags: digit dp Dynamic Programming Number Theory Competitive Programming

Posted on Mon, 02 Nov 2026 16:39:23 +0000 by postmanager