poj3252(组合数学)

题意:

给出一个范围,然后问这个范围内有几个是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));	
	}
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值