Digits Paradise in Hexadecimal (数位dp)

题目链接: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;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值