[蓝桥杯 2021 国 BC] 二进制问题
题目描述
小蓝最近在学习二进制。他想知道
1
1
1 到
N
N
N 中有多少个数满足其二进制表示中恰好有
K
K
K 个 1
。你能帮助他吗?
输入格式
输入一行包含两个整数 N N N 和 K K K。
输出格式
输出一个整数表示答案。
样例 #1
样例输入 #1
7 2
样例输出 #1
3
提示
对于 30 % 30 \% 30% 的评测用例, 1 ≤ N ≤ 1 0 6 , 1 ≤ K ≤ 10 1 \leq N \leq 10^{6}, 1 \leq K \leq 10 1≤N≤106,1≤K≤10 。
对于 60 % 60 \% 60% 的评测用例, 1 ≤ N ≤ 2 × 1 0 9 , 1 ≤ K ≤ 30 1 \leq N \leq 2 \times 10^{9}, 1 \leq K \leq 30 1≤N≤2×109,1≤K≤30 。
对于所有评测用例, 1 ≤ N ≤ 1 0 18 , 1 ≤ K ≤ 50 1 \leq N \leq 10^{18}, 1 \leq K \leq 50 1≤N≤1018,1≤K≤50 。
蓝桥杯 2021 国赛 B 组 H 题(C 组 J 题)。
思路
总结:个人认为数位dp有两种题型:一种是求满足xxx条件(非满足含有xx个1啊0啊)方案数的;另一种是求满足含有xx个1啊,0啊这样的。
本道题就是属于第二种情况。
上图为第一种情况。
上图为第二种情况。
总的来说,对于数位dp的题,我们时常画如图所示的树来做以及分析。
代码
//这道题跟度的数量很像
#include<iostream>
#include<algorithm>
#include<cstring>
#include<vector>
#define int long long
using namespace std;
const int N = 80;
int f[N][N];//表示所有以在i个位置上,放置j个1的组合数
int n,K;
int a[N];
void init(){
for(int i=0;i<=75;i++){
for(int j=0;j<=i;j++){
if(!j)f[i][j]=1;
else{
f[i][j]=f[i-1][j]+f[i-1][j-1];
}
}
}
}
int dp(int n){
if(!n)return 0;
int last=0,ans=0;
int cnt=0;
while(n){
a[++cnt]=n%2;
n/=2;
}
for(int i=cnt;i>=1;i--){
int x=a[i];
if(x>0){//当前为如果放0,则直接跳过
ans+=f[i-1][K-last];//第i位放0
if(x>1){
if(K-last-1>=0)ans+=f[i-1][K-last-1];//第i位放1
break;
}else{
last++;
if(last>K)break;
}
}
if(i==1&&last==K)ans++;
}
return ans;
}
signed main(){
cin>>n>>K;
init();
cout<<dp(n);
//1
//10
//11 ,
//100
//101 ,
//110 ,
//111 ,
return 0;
}