题目链接:abc194_5
题目大意
给你一个长度2e5的16进制表示的数字串S,问你从1到S的这些数中(16进制表示),数字表示正好出现k种数的个数。比如12321,她就有3种数。
思路
数位dp,不过要考虑前导零。他要k种数,可以用state记录状态,比如5(101)就表示他的状态是取了1,3,有2种数。然后呢dp数组中除了要有下标pos,上界限制limit,前导零led外,还要引入一个cnt,表示该状态的种数,因为返回dp数组的前提是dp数组已经修改过,并且没有上界限制了,也就是说,后面可以随便取了,所以只要cnt一样的状态,返回值都会是一样的,所以可以加一维cnt,范围又比状态数小,真香。
ac代码
#include<bits/stdc++.h>
using namespace std;
mt19937_64 rng(time(0));
#define io cin.tie(0);ios::sync_with_stdio(false);
#define ok(x, y) x >= 1 && x <= n && y >= 1 && y <= m
#define debug(x) cout<<#x<<"="<<x<<endl
#define lowbit(x) x&(-x)
#define pii pair<int,int>
#define mk make_pair
#define ll long long
#define ull unsigned long long
#define rs p<<1|1
#define ls p<<1
#define pi acos(-1)
const int maxn = 2e5 + 5;
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;
const ll INF = 1e18;
inline ll read(){
ll p=0,f=1;char c=getchar();
while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
while(c>='0'&&c<='9'){p=(p<<1)+(p<<3)+(c^48),c=getchar();}
return f*p;
}
void print(__int128 x){
if(x<0) {putchar('-'); x=-x;}
if (x>9) print(x/10);
putchar('0'+x%10);
}
int len, k;
int a[maxn], dp[maxn][17][2][2];
int cal(char a){
if(isdigit(a)) return a - '0';
return a - 'A' + 10;
}
int pop(int x){ //算二进制中1的个数
int ans = 0;
while(x){
ans += x % 2;
x /= 2;
}
return ans;
}
int dfs(int pos, int state, bool limit, bool led){
int cnt = pop(state);
if(pos == len) return cnt == k && !led; //除了满足种数是k外还得满足没有前导零
if(cnt > k) return 0; //cnt>k了可以直接返回0,后面的不用跑了
if(!limit && ~dp[pos][cnt][limit][led]) return dp[pos][cnt][limit][led]; //记忆化
int up = limit ? a[pos] : 15; //取上界
int ans = 0; //算答案
for(int i = 0; i <= up; i ++){
// i == 0 && led说明之前有前导零,并且当前是0,那么想到与啥也没取,状态还是0, 否则状态就是state|(1<<i)
ans = (ans + dfs(pos + 1, (i == 0 && led) ? 0 : state | (1 << i), limit && (i == a[pos]), led && (i == 0)) ) % mod;
}
if(!limit) dp[pos][cnt][limit][led] = ans; //没有限制了,后面可以随便取,那么可以存答案了
return ans;
}
void solve(){
string s; cin >> s >> k;
len = s.length();
for(int i = 0; i < len; i ++) a[i] = cal(s[i]); //转数字
memset(dp, -1, sizeof(dp)); //注意初始化
cout << dfs(0, 0, true, true) << endl;
}
int main(){
// freopen("1.in", "r", stdin);
// freopen("std.out", "w", stdout);
// cout << fixed << setprecision(6)
io;
solve();
return 0;
}