题目大意:如果一个数的二进制表示法中0的个数大于等于1的个数,则称这个数为"round number",现在给你一个范围[a,b],让你求出这个范围内的 "round number"的个数。
思路:分别统计小于等于a-1的数,和小于等于b的数中"round number"的个数,然将结果相减即可。对下于等于x的书的统计过程是组合数学的思想,先预处理出不同长度的二进制数中“round number”的个数。求出x的二进制表示长度为len,对于长度小于len的二进制数,可以直接在预处理中找出。对于长度为len的二进制数中"round number"的个数,可以从高位到低位,一次讨论。具体过程,看代码。
#include<cstdio>
#include<cstring>
#include<algorithm>
#define MAX 32
#define ll long long
using namespace std;
ll c[50][50],d[MAX];
ll f(ll x){ //求小于等于x的数中"round number"的个数
if (x == 0 || x == 1) return 1;
ll a[MAX];
ll cnt = 0;
while (x != 0){
a[cnt++] = x % 2;
x /= 2;
}
for (int i = 0,j = cnt-1; i<cnt/2; i++,j--) swap(a[i],a[j]);
ll sum = 0;
for (int i = 1; i<=cnt-1; i++) sum += d[i];
int c0 = 0,u = (cnt+1) / 2;
for (int i = 1; i<cnt; i++){
if (a[i] == 0) c0++;
if (a[i] == 1){
c0++;
int v = u - c0;
for (int j = max(v,0); j<=cnt-i-1; j++){
sum += c[cnt-i-1][j];
}
c0--;
}
}
if (c0 >= u) sum += 1;
return sum;
}
int main(){
memset(c,0,sizeof(c)); //预处理求组合数c[i][j]
for (int i = 0; i<=MAX; i++){
c[i][0] = 1;
c[i][i] = 1;
}
for (int i = 2; i<=MAX; i++)
for (int j = 1; j<i; j++) c[i][j] = c[i-1][j] + c[i-1][j-1];
d[1] = 1; //求二进制数长度为i的"round number"的个数
for (int i = 2; i<=MAX; i++){
d[i] = 0;
for (int j = (i+1) / 2; j<i; j++) d[i] += c[i-1][j];
}
ll n,m;
while (scanf("%lld%lld",&n,&m) != EOF){
printf("%lld\n",f(m) - f(n-1));
}
return 0;
}
本文介绍了一种用于计算特定范围内二进制表示中0的数量大于等于1的数量的数(称为RoundNumber)的数量的方法。通过使用组合数学思想进行预处理,并采用高效的算法实现,解决了这一问题。
6万+

被折叠的 条评论
为什么被折叠?



