[CF55D] Beautiful numbers
前言:
主播曾经只会递推形式的数位dp,最近学会了记忆化搜索的,写点东西记录下。
题目描述:
给定 \(l\), \(r\), \(k\),求 \([l,r]\) 之间的各数位上包含的不同数字不超过 \(k\) 个的所有数的和。答案对 \(998244353\) 取模。
保证 \(1 \leq l \leq r \leq 10, 1 \leq k \leq 10\)。
解题思路:
设计状态 f[n][lim1][lim2][s] 表示到第 \(n\) 位,前面有无非 \(0\) 的数,是否到上界,出现的数的集合为 \(s\) 的数的和,g[n][lim1][lim2][s] 同理,只不过记的是合法的数的个数。
记录前面有无非零的数是为了判断前导零,那么搜索时枚举这一位填什么,判断是否顶到上界,转移显然。
代码实现:
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int mod = 998244353;
int l, r, k, f[30][2][2][1 << 10], g[30][2][2][1 << 10], a[30], fac[30];//第i位,前面有无非0的数,是否到上界,数的集合
int calc1(int x){
int res = 0;
while(x) res += (x & 1), x >>= 1;
return res;
}
void add(int &x, int y){x = (x + y) % mod;}
pair<int, int> dfs(int n, int lim1, int lim2, int s){
if(n == 0){
if(calc1(s) <= k) return make_pair(0, 1);
else return make_pair(0, 0);
}
if(f[n][lim1][lim2][s] != -1) return make_pair(f[n][lim1][lim2][s], g[n][lim1][lim2][s]);
f[n][lim1][lim2][s] = g[n][lim1][lim2][s] = 0;
for(int x = 0; x <= (lim2 ? a[n] : 9); x++){
add(f[n][lim1][lim2][s], dfs(n - 1, lim1 | (x != 0), lim2 & (x == a[n]), (lim1 | (x != 0)) ? (s | (1ll << x)) : s).first);
add(f[n][lim1][lim2][s], ((fac[n - 1] * x % mod) * dfs(n - 1, lim1 | (x != 0), lim2 & (x == a[n]), (lim1 | (x != 0)) ? (s | (1 << x)) : s).second % mod));
add(g[n][lim1][lim2][s], dfs(n - 1, lim1 | (x != 0), lim2 & (x == a[n]), (lim1 | (x != 0)) ? (s | (1ll << x)) : s).second);
}
return make_pair(f[n][lim1][lim2][s], g[n][lim1][lim2][s]);
}
int calc(int x){
memset(f, -1, sizeof f);
memset(g, -1, sizeof g);
int tot = 0;
while(x) a[++tot] = x % 10, x /= 10;
return dfs(tot, 0, 1, 0).first;
}
signed main(){
fac[0] = 1;
for(int i = 1; i <= 20; i++) fac[i] = fac[i - 1] * 10 % mod;
cin >> l >> r >> k;
cout << (calc(r) - calc(l - 1) + mod) % mod << endl;
return 0;
}

浙公网安备 33010602011771号