Educational Codeforces Round 53 (Rated for Div. 2) E. Segment Sum 题解 数位dp

Segment Sum

题目描述

You are given two integers l l l and r r r ( l ≤ r l \le r lr). Your task is to calculate the sum of numbers from l l l to r r r (including l l l and r r r) such that each number contains at most k k k different digits, and print this sum modulo 998244353 998244353 998244353.

For example, if k = 1 k = 1 k=1 then you have to calculate all numbers from l l l to r r r such that each number is formed using only one digit. For l = 10 , r = 50 l = 10, r = 50 l=10,r=50 the answer is 11 + 22 + 33 + 44 = 110 11 + 22 + 33 + 44 = 110 11+22+33+44=110.

输入格式

The only line of the input contains three integers l l l, r r r and k k k ( 1 ≤ l ≤ r < 1 0 18 , 1 ≤ k ≤ 10 1 \le l \le r < 10^{18}, 1 \le k \le 10 1lr<1018,1k10) — the borders of the segment and the maximum number of different digits.

输出格式

Print one integer — the sum of numbers from l l l to r r r such that each number contains at most k k k different digits, modulo 998244353 998244353 998244353.

提示

For the first example the answer is just the sum of numbers from l l l to r r r which equals to 50 ⋅ 51 2 − 9 ⋅ 10 2 = 1230 \frac{50 \cdot 51}{2} - \frac{9 \cdot 10}{2} = 1230 250512910=1230. This example also explained in the problem statement but for k = 1 k = 1 k=1.

For the second example the answer is just the sum of numbers from l l l to r r r which equals to 2345 ⋅ 2346 2 = 2750685 \frac{2345 \cdot 2346}{2} = 2750685 223452346=2750685.

For the third example the answer is 101 + 110 + 111 + 112 + 113 + 114 + 115 + 116 + 117 + 118 + 119 + 121 + 122 + 131 + 133 + 141 + 144 + 151 = 2189 101 + 110 + 111 + 112 + 113 + 114 + 115 + 116 + 117 + 118 + 119 + 121 + 122 + 131 + 133 + 141 + 144 + 151 = 2189 101+110+111+112+113+114+115+116+117+118+119+121+122+131+133+141+144+151=2189.

题面翻译

给定 l , r , k l, r, k l,r,k,求 [ l , r ] [l, r] [l,r] 之间的各数位上包含的不同数字不超过 k k k 个的所有数的和。答案对 998244353 998244353 998244353 取模。

数据规模与约定

1 ≤ l ≤ r ≤ 1 0 18 1 \leq l \leq r \leq 10^{18} 1lr1018
1 ≤ k ≤ 10 1 \leq k \leq 10 1k10

样例 #1

样例输入 #1

10 50 2

样例输出 #1

1230

样例 #2

样例输入 #2

1 2345 10

样例输出 #2

2750685

样例 #3

样例输入 #3

101 154 2

样例输出 #3

2189

原题

CF——传送门
洛谷——传送门

代码

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

const ll mod = 998244353;
int k;                              // 条件:不超过k个不同数字
pair<int, int> dp[20][1 << 12][12]; // dp记录[pos][mask][last]状态下的满足条件的个数
int a[20];                          // a[]记录数字串

// 检查此时是否超过了k个不同数字
bool check(ll mask)
{
    int tot = 0;
    for (int i = 0; i <= 9; i++)
    {
        if ((mask >> i) & 1)
            tot++;
    }
    if (tot > k)
        return 0;
    else
        return 1;
}

// 进行答案的运算
void add(pair<int, int> &res, pair<int, int> dfs_ret, int num, int pos)
{
    res.first += dfs_ret.first;
    res.first %= mod;

    res.second += 1ll * dfs_ret.first % mod * num % mod * ((ll)pow(10, pos) % mod) % mod;
    res.second %= mod;
    res.second += dfs_ret.second;
    res.second %= mod;
}

// pos为此时的位置,mask是对0~10各个数是否存在的状态压缩,last记录上一个数,bound表示前面每一位是否都是上界,lead_0表示是否前面全是0
pair<int, int> dfs(int pos, ll mask, int last, bool bound, bool lead_0) // pair<int,int>的first记录当前方案数,second记录当前总和
{
    if (!check(mask))
        return {0, 0};
    if (pos == 0)                                             // 枚举完每一位时返回
        return {1, last};                                     // 方案数+1,总和+last
    if (!bound && !lead_0 && dp[pos][mask][last].first != -1) // 读取记忆(doge
        return dp[pos][mask][last];
    int max_num; // 可枚举的该位的数的上界
    if (bound)
        max_num = a[pos];
    else
        max_num = 9;
    pair<int, int> res = {0, 0}; // 统计方案数和总和
    for (int i = 0; i <= max_num; i++)
    {
        if (lead_0 && i == 0)
            add(res, dfs(pos - 1, mask, last, bound && (i == a[pos]), 1), 0, 0);
        else
            add(res, dfs(pos - 1, mask | (1 << i), i, bound && (i == a[pos]), 0), last, pos);
    }
    if (!bound && !lead_0) // 没在边界时,记录下该状态对应的答案,记忆化
        dp[pos][mask][last] = res;
    return res;
}

int solve(ll x)
{
    int len = 0; // 数字长度
    while (x)
    {
        a[++len] = x % 10;
        x /= 10;
    }
    return dfs(len, 0, 0, 1, 1).second;
}

int main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);

    memset(dp, -1, sizeof(dp)); // 将dp数组初始化为-1,表示对应状态的答案目前还未计算出
    ll l, r;
    cin >> l >> r >> k;
    cout << ((solve(r) - solve(l - 1)) % mod + mod) % mod << '\n';

    return 0;
}
  • 39
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值