题意:
给出一个范围,然后问这个范围内有几个是round number;
round number也就是转化二进制后,0的个数大于等于1的个数;
思路:
这是一个组合计数的问题;我们拿一个二进制长度为10的数举例子;
如果长度为10;那么所有长度为10以下的数字,肯定都是在范围内的,那么我们就要找出所有符合条件的数;
首先9位的,那么需要的0的个数是5个以上;那么长度为9的,出了第一位必须是1剩下八位我们就可以随意组合选出5个以上的位置填充0;
也就是C(8, 5) + C(8, 6) + C(8, 7) + C(8 , 8);
同理所有长度小于10的全都累加起来;
然后要单独讨论长度为10的,因为这个长度有的数是超出范围的;
然后我们计算的时候,就从高位往地位枚举;如果出现0,就把0的数量加加;
如果出现1,我们就把这一位置成0,然后剩下的位用上面的办法算出所有组合;
例如1001100110
长度为10,那么需要的0的个数就是5个以上;我们从第二位开始(第一位肯定是1不能变)然后到第四位时,出现了1;那如果这一位变成0,那么剩下的位数就能随意组合棵,因为肯定比原来数字小,因为原来的位置在比较高的一个位是1;所以把第四位变成0后,0的个数就是3个了,那么剩下的6位里还需要2个以上的0;
所以再去加C(6,2)+ C(6,3) + C(6,4) + C(6,5) + C(6,6);
加完后要将0的个数再减一,因为我们把第四位变成0,只是为了计算这一种情况,然后继续往低位找过去;
#include<cstdio>
#include<cstring>
#define ll long long
const int N = 65;
ll c[N][N];
ll b[N];
ll sum;
ll l, r;
int num0, num1;
void getC(int n) {
memset(c, 0, sizeof(c));
c[0][0] = 1;
for(int i = 0; i <= n; i++) {
c[i][0] = c[i][i] = 1;
for(int j = 0; j < i; j++) {
c[i][j] = (c[i - 1][j] + c[i - 1][j - 1]);
}
}
}
ll cul(ll x) {
int len = 0;
sum = num0 = 0;
while(x != 0) {
b[len++] = x % 2;
x /= 2;
}
for(int i = 1; i < len; i++) {
for(int j =(i + 1) / 2; j < i; j++)
sum += c[i - 1][j];
}
for(int i = len - 2; i >= 0; i--) {
if(b[i]) {
num0++;
int k = (len + 1)/2 - num0;
k = k < 0 ? 0 : k;
for(int j = k; j <= i; j++)
sum += c[i][j];
num0--;
}else
num0++;
}
return sum;
}
int main() {
getC(N);
while(scanf("%lld%lld", &l, &r) == 2) {
printf("%lld\n", cul(r + 1) - cul(l));
}
}