abc336 E - Digit Sum Divisible 題解 數位DP

題目鏈接:https://atcoder.jp/contests/abc336/tasks/abc336_e

題目大意:

我們定義一個整數 \(n\)數位和\(n\) 的十進制表示中的各位上的數字之和。比如:整數 \(2024\) 的數位和爲 \(2 + 0 + 2 + 4 = 8\)

一個正整數 \(n\) 被稱作一個 好數 如果 \(n\) 能被它的數位和整除。舉例,\(2024\) 是好數,因爲它能被它的數位和 \(8\) 整除。

給你一個整數 \(N(1 \le N \le 10^{14})\),請你計算有多少 \(\le N\) 的好數。

輸入 \(N\),輸出 \(\le N\) 的好數個數。

解題思路:

枚舉數位和 \(X\)\(X\) 最大也就 \(14 \times 9\)),然後對於每一個 \(X\),做一遍數位DP。

定義狀態 \(f_{p, x, y}\) 表示當前在第 \(p\) 位,到第 \(p\) 位爲止的數模 \(X\)\(x\),到第 \(p\) 位爲止的數的數位和爲 \(y\) 對應的數字個數。

則最終(邊界條件)只有當 \(p = -1\) 時,只有 \(x = 0\)\(y = X\) 的狀態對應的個數爲 \(1\)

示例程序:

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

int a[22], X;
// f[p][x][y] 第 p 爲,模 X = x,數位和 y
long long N, ans, f[22][140][140][2];
bool vis[22][140][140][2];

long long dfs(int p, int x, int y, bool limit) {
    if (p < 0) {
        return x == 0 && y == X;
    }
    if (vis[p][x][y][limit]) return f[p][x][y][limit];
    vis[p][x][y][limit] = true;
    long long &res = f[p][x][y][limit];
    res = 0;
    int up = limit ? a[p] : 9;
    for (int i = 0; i <= up; i++) {
        int xx = (x * 10 + i) % X, yy = y + i;
        res += dfs(p-1, xx, yy, limit && i == up);
    }
    return res;
}

long long cal(long long num) {
    memset(vis, 0, sizeof vis);
    int p = 0;
    while (num) {
        a[p++] = num % 10;
        num /= 10;
    }
    return dfs(p-1, 0, 0, 1);
}

int main() {
    scanf("%lld", &N);
    for (X = 1; X <= 14 * 9; X++)
        ans += cal(N);
    printf("%lld\n", ans);
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章