题目:传送门
题意:问在区间 [ 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 ", ans); } int main() { solve(); return 0; }