做题记录 2021.10.17 洛谷P2431

题目链接

首先,看到 1,2,4,8…… 这样的序列一定要想到二进制。不难发现,此题就是要我们求[a,b]的所有数中,二进制1最多的那个数的二进制有多少个1。

前置知识:__builtin_popcount:GCC中内置的函数,可用于快速计算二进制中1的个数。其对应的64位整数版本为__builtin_popcountll。
当然也可以自行实现,但以我的水平只能实现一个O(logn)的。

不难发现:如果 ⌊ l o g 2 a ⌋ ≠ ⌊ l o g 2 b ⌋ \lfloor log_2a\rfloor\neq \lfloor log_2b\rfloor log2a=log2b,那么答案就为 ⌊ l o g 2 b ⌋ \lfloor log_2b\rfloor log2b ⌊ l o g 2 b ⌋ \lfloor log_2b\rfloor log2b+1(具体见代码)

if(lg2a!=lg2b) {
   	if(b+1==(1ll<<(lg2b+1)))	res=lg2b+1;
   	else	res=lg2b;
}

而二者相等时,可以发现一个偶数+1或-1得到的奇数中二进制1的个数不会减少。所以只要遍历所有奇数,并在遍历过程中更新答案即可。

if(a==b)	res=__builtin_popcountll(a);  //注意a==b的特殊情况
if(b%2==0)	 b--;  //注意只遍历奇数
for(long i=b; i>=a; i-=2) {  //注意数据类型为long long
	int x=__builtin_popcountll(i);
	res=max(res,x);
}

然而,在最坏情况下(考虑a= 2 k 2^k 2k,b= 2 k + 1 2^{k+1} 2k+1-1),这种分类讨论的复杂度可高达O(mn)(其中m=1/2,n=b-a),这还是在认为popcount函数复杂度为1的前提下。虽然提交可以通过,但这是因为数据比较水。


正确解法:从a的最低位开始,贪心地将每一位0改为1,直到a>=b
代码如下:

if(lg2a!=lg2b) {
   	if(b+1==(1ll<<(lg2b+1)))	res=lg2b+1;
   	else	res=lg2b;
}
else {
	res=__builtin_popcountll(a);
	for(int i=0; i<=lg2a&&(a|(1ll<<i))<=b; i++) {
		if(!((a>>i)&1)) {  //如果某一位为0,就贪心地将其改为1
			a|=1ll<<i;  //注意将二进制的0变为1的方法
			res++;
		}
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值