洛谷 P4999 烦人的数学作业

思路:

数位dp题

\(f_{i,j}\) 表示第 \(i\) 位首数码为 \(j\) 涵盖的数的数位之和

那么有转移方程

\[f_{i,j} = \sum_{k=0}^{9}(f_{i-1,k} + j \times c_{i-1,k}) \]

其中 \(c_{i,k}\) 代表第 \(i\) 位首位数码为 \(k\) 的数的个数

当然 , 也可以用\(dfs\)

将大数按位展开 , 就是一个多叉树 , 设每个边的权为当前位的数字1-9/top , 那么设dfs某个节点可以获得该节点下所有数字的位数和 :

那么dfs树根就是所求结果

如何求dfs树根 ? 需要知道树根相连节点的所有子树dfs结果 , 然后累加

如何知道子树的dfs结果 ? 树根需要提供树根到子树的边权值 , 比如712 , 考虑提供7 , 那么后面从700到799的情况都是有一个"7"在前面顶着的

再如82500到82599 , 00到99的情况枚举中都有一个"8 + 2 + 5"在前面顶着

所以要求cur位的值 , 需要知道cur到根节点的和sum , 以及当前需要到达的所有地方(0-top/9)

等到cur为0 时 , 此时sum就是一条树的路径 , 返回sum即可

那么写dfs的时候 , 决定一个非limit的可能值时 , dfs的携带量只有cur和sum , 所以理所应当建立cur和sum的记忆化搜索

当然 , 也可以参照https://www.cnblogs.com/swyblog/articles/18786587然后用算出的数码个数来计算

代码:

dp版本

#include<bits/stdc++.h>
using namespace std;
#define int long long int
inline int read() {
    int ans = 0, f = 1;
    char ch = getchar();
    while (ch < '0' || ch > '9') {
        if (ch == '-')f = -1;
        ch = getchar();
    }
    while (ch <= '9' && ch >= '0') {
        ans = ans * 10 + ch - '0';
        ch = getchar();
    }
    return ans * f;
}
const int mod = 1e9+7;
const int N = 20;
int a[N];
int f[N][10],c[N][10];
void init() {
    for (int i =0; i<= 9; i++) {
        f[1][i] = i;
        c[1][i] = 1;
    }
    for (int i =2; i< N; i++) {
        for (int j = 0; j<= 9; j++) {
            for (int k = 0; k< 10; k++) {
                c[i][j] += c[i-1][k];
                c[i][j]= (c[i][j] + mod)%mod;
            }
        }
    }
    for (int i =2; i< N; i++) {
        for (int j = 0; j<= 9; j++) {
            for (int k = 0; k< 10; k++) {
                f[i][j] += f[i-1][k] +mod;
                f[i][j] %= mod;
                f[i][j] +=( j * c[i-1][k]+mod)%mod + mod;
                f[i][j]= (f[i][j] + mod) %mod;
            }
        }
    }
}
int dp(int x) {
    int cnt =0,ans = 0;
    while (x) {
        a[++cnt] = x%10;
        ans = (ans + a[cnt]) % mod;
        x/=10;
    }
    int pre = 0;
    for (int i =cnt; i>0; i--) {
        int now = a[i];
        for (int j = 0;j< now; j++) {
            ans = (ans + f[i][j] + mod)%mod;
            ans = (ans +( pre * c[i][j] + mod)%mod + mod)%mod;
        }
        pre+=now;
    }
    return ans;
}
void solve() {
    int a = read(),b =read();
    cout<<(dp(b) % mod- dp(a-1)%mod + mod) % mod<<"\n";
}
signed main() {

    int t = read();
    init();
    while (t--) solve();



    return 0;
}

dfs版本


posted @ 2025-03-20 21:57  Guaninf  阅读(13)  评论(0)    收藏  举报