题意:
- 对于一个二进制数,若其含 ‘0’ 的个数不少于 ‘1’ 的个数,则称之为 ‘round number’。现输入一个闭区间的左右端点,求此区间中 ‘round number’ 的数量。
- 数据范围:2e9,使用 ‘int’ 即可。
前言:
-
十分感谢EXP站,大概接下来很长一段时间就要在这里耕耘了。题解估计大多相似,以后不再提了。
-
组合数第一题。
-
首先提出一些基本性质:参见上面的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)
-
对于全部 ‘位数 < len’ 的二进制数 x,它的二进制形式除最高位必为‘1’,其余位数可随意选取,均能保证 x < left。欲使 x 为 ‘round number’,则须在 ‘x_len - 1’ 个位置中选择不少于 ‘x_len / 2’ 个置 ‘0’ 即可满足 ‘round number(left)’。
-
对于全部 **‘位数 == len’**的二进制数 x,我们从左到右遍历一遍,每次将一个 ‘1’ 置 ‘0’,则它后面的位数可随意选取,均能保证 x < left。那么欲使 x 为 ‘round number’,需要在后面的位数中选择多少个数字置 ‘0’ 呢:
设 index 表示从低到高(从右往左数)的第 index 位,zero_cnt 代表 index左边(不含index)共有多少个 ‘0’;设 a 为在右边填入 ‘1’ 的个数,b 为在右边填入 ‘0’ 的个数。
有两个关系:
- index = a + b
- 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;
}