题目:传送门
题意:问在区间 [ L, R ] 中使用的数字不超过 k 种的数的和是多少,例如区间 [ 10, 50 ] 中使用的数字不超过 1 种的数的和是 11 + 22 + 33 + 44 = 110.
1 <= L <= R < 1e18, 1 <= k <= 10, 输出答案对 998244353 取模。
题解:显然数位DP,不过这里要算的是数的和,不是数的个数,那我们再维护一个变量就行啦,维护一下每个位上的数的贡献。
#include <bits/stdc++.h>
#define LL long long
#define mem(i, j) memset(i, j, sizeof(i))
#define rep(i, j, k) for(int i = j; i <= k; i++)
#define dep(i, j, k) for(int i = k; i >= j; i--)
#define pb push_back
#define make make_pair
#define INF INT_MAX
#define inf LLONG_MAX
#define PI acos(-1)
using namespace std;
const int N = 1e6 + 5, mod = 998244353;
int a[20];
int k;
LL p[25];
pair < LL, LL > dp[20][1 << 15];
pair < LL, LL > dfs(int pos, int statu, bool zero, bool limit) {
if(!pos) return make(__builtin_popcount(statu) <= k, 0); /// 贡献在前面已经算过
if(!limit && !zero && dp[pos][statu].first != -1) return dp[pos][statu];
int up = limit ? a[pos] : 9;
pair < LL, LL > ans = make(0, 0);
rep(i, 0, up) {
pair < LL, LL > res;
if(zero && i == 0) res = dfs(pos - 1, statu, zero, limit && i == up); ///前导0,这些0不算用过0.
else res = dfs(pos - 1, statu | (1 << i), zero && i == 0, limit && i == up);
ans.first = (ans.first + res.first) % mod;
ans.second = (ans.second + res.second + i * res.first % mod * p[pos - 1] % mod) % mod;
}
if(!limit && !zero) dp[pos][statu] = ans;
return ans;
}
LL cal(LL x) {
int tot = 0;
while(x) {
a[++tot] = x % 10;
x = x / 10;
}
mem(dp, -1);
return dfs(tot, 0, 1, 1).second;
}
void solve() {
LL l, r;
p[0] = 1LL;
rep(i, 1, 20) p[i] = (p[i - 1] * 10LL) % mod;
scanf("%lld %lld %d", &l, &r, &k);
LL ans = (cal(r) - cal(l - 1) + mod) % mod;
printf("%lld\n", ans);
}
int main() {
solve();
return 0;
}
来源:https://www.cnblogs.com/Willems/p/12389841.html