题意:给你一个数字n,让你求1-n之间,十六进制下恰好有k个不同数字的数有几个。
思路:一眼就看出了是数位dp。但是没有仔细去想非常的遗憾。赛后发现其实只需要用一个16位的01串去表示这个数字有没有被用过就行了。
个人喜欢用记忆化搜索写数位dp。给出dp数组。dp[16位的01串][当前枚举数位][是否有前导0][是否满足大小限制]
#include <bits/stdc++.h> #define DEBUG if(0) typedef long long ll; using namespace std; const int maxSz = 2e5, maxCnt = 16; const ll mod = 1e9 + 7; ll dp[maxSz][maxCnt + 1][2][2]; char s[maxSz + 1]; int a[maxSz]; int sz, k; ll solve(int state, int pos, bool lmt, bool lead) { int cnt = __builtin_popcount(state); if (cnt > k) return 0; if (pos == sz) return cnt == k && lead; ll &ans = dp[pos][cnt][lmt][lead]; if (ans != -1) return ans; ans = 0; int llmt = (lmt ? a[pos] : 15); for (int i = 0; i <= llmt; i++) ans = (ans + solve((!i && !lead) ? state : state | (1 << i), pos + 1, lmt & (i == llmt), lead | i)) % mod; return ans; } int main() { cin >> s >> k; sz = strlen(s); for (int i = 0; i < sz; i++) { a[i] = s[i] - (isdigit(s[i]) ? '0' : 'A' - 10); } memset(dp, -1, sizeof(dp)); cout << solve(0, 0, 1, 0) << endl; return 0; }