E. Segment Sum
E. Segment Sum
You are given two integers $l$ and $r$ ($l \le r$). Your task is to calculate the sum of numbers from $l$ to $r$ (including $l$ and $r$) such that each number contains at most $k$ different digits, and print this sum modulo $998244353$.
For example, if $k = 1$ then you have to calculate all numbers from $l$ to $r$ such that each number is formed using only one digit. For $l = 10, r = 50$ the answer is $11 + 22 + 33 + 44 = 110$.
Input
The only line of the input contains three integers $l$, $r$ and $k$ ($1 \le l \le r < 10^{18}, 1 \le k \le 10$) — the borders of the segment and the maximum number of different digits.
Output
Print one integer — the sum of numbers from $l$ to $r$ such that each number contains at most $k$ different digits, modulo $998244353$.
Examples
input
10 50 2
output
1230
input
1 2345 10
output
2750685
input
101 154 2
output
2189
Note
For the first example the answer is just the sum of numbers from $l$ to $r$ which equals to $\frac{50 \cdot 51}{2} - \frac{9 \cdot 10}{2} = 1230$. This example also explained in the problem statement but for $k = 1$.
For the second example the answer is just the sum of numbers from $l$ to $r$ which equals to $\frac{2345 \cdot 2346}{2} = 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$.
解题思路
今天写专门挑了几道数位 dp 的题目来做,选这题来写篇题解吧。
定义 $dp(N)$ 表示前 $N$ 个数中满足十进制下最多有 $k$ 个不同数字的数的总和,因此答案就是 $dp(r) - dp(l-1)$。
由于需要知道某个数用了哪些数字,因此必然会用到状态压缩。定义状态 $f(i,j,k,u)$ 表示满足以下条件的数(用 $n$ 来表示)的总和:
- $n$ 的数位大小为 $i$,即满足 $0 \leq n < 10^i$。
- 若 $n \leq N \bmod 10^i$,则 $j = 1$,否则 $j = 0$。
- 若 $n$ 的最高位是 $0$,则 $k = 1$,否则 $k = 0$。
- $n$ 由 $u$ 的二进制下为 $1$ 的位所对应的数字构成,例如 $u = (10011)_2$,那么 $n$ 由数字 $0$,$1$,$4$ 构成。
在得到状态转移方程前,还需要再定义状态 $g(i,j,k,u)$ 表示满足以上相同条件的数(用 $n$ 来表示)的数量。
当确定了状态 $f(i,j,k,u)$ 和 $g(i,j,k,u)$ 后,就可以转移到数位大小为 $i+1$ 的状态,根据第 $i+1$ 位是哪个数字 $v$ 来进行状态划分,有状态转移方程
$$f(i,j,k,u) + v \cdot 10^i \cdot g(i,j,k,u) \longrightarrow f(i+1, \, [v<p_i \; \text{or} \; v=p_i \; \text{and} \; j], \, [!v], \, u \mid 2^v)$$
$$g(i,j,k,u) \longrightarrow g(i+1, \, [v<p_i \; \text{or} \; v=p_i \; \text{and} \; j], \, [!v], \, u \mid 2^v)$$
其中 $p_i$ 就是 $N$ 在十进制下的第 $i$ 位(从右往左,最低位为第 $0$ 位)。假设 $N$ 在十进制下有 $sz$ 位,$\text{count}(x)$ 表示 $x$ 在二进制下 $1$ 的个数,那么最终答案就是$$dp(N) = \sum\limits_{i=1}^{sz}{\sum\limits_{j=1}^{2^{10}-1}{[\text{count}(j) \leq m] \cdot f(i,1,0,0)}} \; + \; \sum\limits_{i=1}^{sz - 1}{\sum\limits_{j=1}^{2^{10}-1}{[\text{count}(j) \leq m] \cdot f(i,0,0,0)}}$$
AC 代码如下,时间复杂度为 $O(2^{10} \cdot \log{N})$:
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 25, M = 1 << 10, mod = 998244353;
LL l, r;
int m;
int p[N], sz;
int q[N];
int f[N][2][2][M], g[N][2][2][M];
int get(LL x) {
if (!x) return 0;
sz = 0;
while (x) {
p[sz++] = x % 10;
x /= 10;
}
q[0] = 1;
for (int i = 1; i < sz; i++) {
q[i] = q[i - 1] * 10ll % mod;
}
memset(f, 0, sizeof(f));
memset(g, 0, sizeof(g));
g[0][1][0][0] = 1;
for (int i = 0; i < sz; i++) {
for (int j = 0; j <= 1; j++) {
for (int k = 0; k <= 1; k++) {
for (int u = 0; u < M; u++) {
for (int v = 0; v <= 9; v++) {
int &x = f[i + 1][v < p[i] || v == p[i] && j][!v][u | 1 << v];
int &y = g[i + 1][v < p[i] || v == p[i] && j][!v][u | 1 << v];
x = (x + 1ll * v * q[i] * g[i][j][k][u] + f[i][j][k][u]) % mod;
y = (y + g[i][j][k][u]) % mod;
}
}
}
}
}
int ret = 0;
for (int i = 1; i <= sz; i++) {
for (int j = 1; j < M; j++) {
if (__builtin_popcount(j) <= m) {
ret = (ret + f[i][1][0][j]) % mod;
if (i < sz) ret = (ret + f[i][0][0][j]) % mod;
}
}
}
return ret;
}
int main() {
scanf("%lld %lld %d", &l, &r, &m);
printf("%d\n", (get(r) - get(l - 1) + mod) % mod);
return 0;
}
参考资料
Educational Codeforces Round 53 Editorial:https://codeforces.com/blog/entry/62742
本文来自博客园,作者:onlyblues,转载请注明原文链接:https://www.cnblogs.com/onlyblues/p/17788090.html