POJ 3252【Round Numbers】

4 篇文章 0 订阅

题意:

  • 对于一个二进制数,若其含 ‘0’ 的个数不少于 ‘1’ 的个数,则称之为 ‘round number’。现输入一个闭区间的左右端点,求此区间中 ‘round number’ 的数量。
  • 数据范围:2e9,使用 ‘int’ 即可。

前言:

  • 十分感谢EXP站,大概接下来很长一段时间就要在这里耕耘了。题解估计大多相似,以后不再提了。

  • 感谢zhengnanlee的BLOG

  • 组合数第一题。

  • 首先提出一些基本性质:参见上面的BLOG。

思路:

  • 函数 round_number(int n)【0 , n) 内的 ‘round number’ 数量。则本题目标可转化为求round_number(right + 1) - round_number(left)
  • 定义函数 convert_decimal_to_binary(int n) 将输入的十进制端点变为二进制数,将它的数字(0/1)存在 binary[32] 中。
  • 接下来我们计算 round number(left):(设 left 二进制数位为 len
    1. 对于全部 ‘位数 < len’ 的二进制数 x,它的二进制形式除最高位必为‘1’,其余位数可随意选取,均能保证 x < left。欲使 x‘round number’,则须在 ‘x_len - 1’ 个位置中选择不少于 ‘x_len / 2’ 个置 ‘0’ 即可满足 ‘round number(left)’

    2. 对于全部 **‘位数 == len’**的二进制数 x,我们从左到右遍历一遍,每次将一个 ‘1’‘0’,则它后面的位数可随意选取,均能保证 x < left。那么欲使 x‘round number’,需要在后面的位数中选择多少个数字置 ‘0’ 呢:

      index 表示从低到高(从右往左数)的第 index 位,zero_cnt 代表 index左边(不含index)共有多少个 ‘0’;设 a 为在右边填入 ‘1’ 的个数,b 为在右边填入 ‘0’ 的个数。

      有两个关系:

      1. index = a + b
      2. zero_cnt + 1 + b >= a + len - index - (zero_cnt + 1)

      可推得:b >= len / 2 - (zero_cnt + 1)

      具体到代码中,还需根据 len 的奇偶性分析 b 的具体程序公式。

代码:

//716K		16MS
//left、right不可以做全局变量名

#include <iostream>
#include <cstring>

using namespace std;

int LEFT , RIGHT;
int C[32][32];
int binary[32];int len;
//C_31_15约等于3e8

void C_n_m(){
	memset(C , 0 , sizeof(C));
	for(int i=0;i<=31;i++){
		for(int j=0;j<=i;j++){//可以优化
			if(!j || i==j)
				C[i][j] = 1;
			else
				C[i][j] = C[i-1][j] + C[i-1][j-1];//需要保证i > j
		}
	}
}

void convert_decimal_to_binary(int decimal){
	memset(binary , 0 , sizeof(binary));
	len = 0;
	while(decimal){
		binary[++len] = (decimal & 1);
		decimal>>=1;
	}
	return ;
}

int round_number(int decimal){
	convert_decimal_to_binary(decimal);
	int ans = 0;
	
	//长度小于len的round_number
	for(int i=1;i<len;i++)
		for(int j=i-1;j>=(i+1)/2;j--)//没有包括0
			ans += C[i-1][j];
	
	//长度等于len的round_number
	int zero_cnt=0;
	for(int index=len-1 ; index>=1 ; index--)
		if(binary[index])
			for(int j=(len+1)/2 - (zero_cnt+1) ; j<=(index-1) ; j++)
				ans += C[index-1][j];
		else
			zero_cnt++;
	return ans;
}

int main(){
	cin>>LEFT>>RIGHT;
	C_n_m();
	cout<<round_number(RIGHT+1) - round_number(LEFT)<<endl;
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值