2 条题解

  • 1
    @ 2026-6-3 12:13:33

    这个题的一个解法是数位dp

    就是直接统计每个数位的出现频率再乘上本身

    同时有一个类似的题

    题目链接

    数位dp推荐都写成dfs形式的,因为递推式的数位dp是很难处理边界条件的

    刚开始学数位dp我建议把状态全部记上,

    比如常见的有第ii数位,前导零,上下界,

    代码如下

    #include <bits/stdc++.h>
    #define int long long
    using namespace std;
    int a[20], memo[16][16][2][2];
    int dfs(int p,int sm,bool ld,bool lm,int tg) {
        if (p==0) {
            return sm;
        }
        // 这里的变量名含义,p是目前处理到第几个数位,
        // sm是当前该数位(也就是tg)一共出现了几次
        // ld就是是否含有前导0
        // lm就是是否贴着上界
    
        //处理上下界的原因是因为枚举该位的数字
    
        if (memo[p][sm][ld][lm]!=-1) {
            return memo[p][sm][ld][lm];
        }
        int res=0;
        int up=lm ? a[p] : 9;
        //如果是27000,如果最高位不是2,下一位枚举的数字可以是0-9,但是如果最高位是2,下一位枚举的数字只能是0-7
        for (int i=0;i<=up;i++) {
            int nsm=sm+ ((i==tg)&&!(ld && i==0));
            //处理前导0的原因是因为数字不含前导0,比如9,如果有前导0计数可能是009,会干扰到0的计数
    
            res+=dfs(p-1,nsm,ld && (i==0),lm && (i==up),tg);
            //这里如果下一位依然是0并且仍然处于前导0状态仍然记作前导0,
            //
        }
        return memo[p][sm][ld][lm]=res;
        //返回的时候直接把状态存上
    }
    int sol(int n, int tg) {
        int len = 0;
        while (n) a[++len] = n % 10, n /= 10;
        memset(memo, -1, sizeof(memo));
        return dfs(len,0,true,true,tg);
    }
    signed main() {
        ios::sync_with_stdio(false); cin.tie(nullptr);
        int t;
        cin>>t;
        while(t--) {
            int n;
            cin>>n;
            int ans=0;
            for (int i=0;i<=9;i++) {
                ans+=sol(n,i)*i;
            }
            cout<<ans<<'\n';
        }
    }
    

    然后恭喜你收到了一个TLE的代码

    因为这个地方的 TT 很大,并且 TT 不受 NN 的约束

    所以数位dp时我们不能memset的方式来解决初始化

    memset是因为我们我们在处理0-9每个数位以及每组数据之间都要重置一下,

    我们可以直接把状态全部存完并且加上vis数组规避memset

    #include <bits/stdc++.h>
    #define int long long
    using namespace std;
    int a[20];
    int memo[20][20][2][2][10];//加上数位的状态
    bool vis[20][20][2][2][10];
    int dfs(int p, int sm, bool ld, bool lm, int tg) {
        if (p == 0) {
            return sm;
        }
        if (vis[p][sm][ld][lm][tg]) {
            return memo[p][sm][ld][lm][tg];
        }
        int res = 0;
        int up = lm ? a[p] : 9;
        for (int i = 0; i <= up; i++) {
            int nsm = sm + ((i == tg) && !(ld && i == 0));
            res += dfs(p - 1, nsm, ld && (i == 0), lm && (i == up), tg);
        }
        if (!lm) {
            vis[p][sm][ld][lm][tg] = true;
            return memo[p][sm][ld][lm][tg] = res;
        }
        return res;
    }
    int sol(int n, int tg) {
        int len = 0;
        while (n) a[++len] = n % 10, n /= 10;
        return dfs(len, 0, true, true, tg);
    }
    signed main() {
        ios::sync_with_stdio(false); cin.tie(nullptr);
        int t;
        cin >> t;
        while(t--) {
            int n;
            cin >> n;
            int ans = 0;
            for (int i = 0; i <= 9; i++) {
                ans += sol(n, i) * i;
            }
            cout << ans << '\n';
        }
        return 0;
    }
    

    其实数位dp写多了你就能发现这东西其实是轮椅

    信息

    ID
    1166
    时间
    1000ms
    内存
    256MiB
    难度
    5
    标签
    (无)
    递交数
    22
    已通过
    5
    上传者