HDU 5969 最大的位或

首先感谢这位前辈的博客……这里详细说下自己的理解。

http://blog.csdn.net/qq_33402621/article/details/53047895


这题主要是思路……



输入两个整数l和r,求两个数满足l<=x<=y<=r,使得x | y最大。


另ans = x | y,那么ans肯定的位数肯定和r一样,因为位数越多,这个数就越大,位数最多的肯定是r。


所以我们先把r用二进制表示,把每一位存在一个数组里面。


然后从二进制的最右边开始(代码里面因为是每次把这个数的二进制最右边的数拿出来放到数组ans的第0,1,2,3的位置,所以遍历的时候从0~ansCnt)遍历。


遇到某一位是1,说明这一位已经可以取到1,不用管;遇到0,说明这一位要试试能不能让它进行或操作后变成1。


怎么试呢?一个个遍历?数据范围是10的18次方,肯定会超时。


所以我们这样,假如ans数组的第i个元素是0, 求一个数num =r - sum - 1,sum表示ans中从0到i - 1的数字表示的值,比如ans的前4位是1,0, 1,0,那么i=2的时候,sum就是


101这个二进制数对应是十进制数,也就是5 。


这个num就是与之进行或操作后可以让这一位变成1的,举个例子。假如r的二进制是 11110111,现在要求能使那个0变成1的数,那么所求的数肯定那一位就必须是1,所求的数

就是r - sum - 1,sum 就是111,r - sum = 11110000,然后再-1,11101111,可以看到对应的那一位是1 。


这个num一定比r小,因为sum >= 0,而num = r - sum - 1


这样求出来的num是    满足对应位是1 且 小于r    的数里面最大的。因为求num的时候,r - sum,r左边的几位都没有动,也不能动,只是右边全变成了0,然后-1,这样右边全变

成了1,也就是最大,全1当然最大,对吧?(如果把前面的几位也变了,会不会比num更大且满足条件呢?不会的,因为如果把前面的0变成了1,那么肯定就比r大了,如果把前

面的1变成0,又肯定比num小。举个不太恰当的十进制的例子,r=2345012,要把0变成9,那么应该是2345012 - 12 - 1 = 2344999,如果把前面的2345任何一个变大了,就肯定比r大,如果把4变成了3,那么得2335999,比2344999小……)


为什么要求出最大的满足条件的数呢?因为要和 l(输入的数据) 比较,如果最大的能使这一位变成1的数字都比l小,说明没有数能使这一位变成1 。


如果num >= l,说明这一位可以变成1,就把ans数组的这一位变成1 l;否则就不管


然后看下一位。


最后ans所表示的数就是答案。


可能有人怀疑,这样每次算的num都不同,而题目规定只能取两个数,会不会不对?


不会的。因为求最左边那个0的时候,i = a, num的0到a - 1位都变成了1 。


那为什么不一开始就求最左边的0呢,因为这时求出来的num可能比l小,还是要看其他位。



#include<iostream>

#define LEN (64 + 5)

using namespace std;

long long l, r;
int ans[LEN];
int ansCnt;
long long mi[LEN];
long long opMi(int num)
{
	if(mi[num])
		return mi[num];
	long long sum = 1;
	for(int i=1; i<=num; i++)
		sum *= 2;
	return mi[num] = sum;
}

int main()
{
	int t;
	int i;
	cin>>t;
	while(t--)
	{
		ansCnt = 0;
		cin>>l>>r;
		long long temp = r;
		while(temp)
		{
			ans[ansCnt++] = temp & 1;
			temp >>= 1;
		}
		long long sum = 0;
		for(i=0; i<ansCnt; i++)
		{
			if(ans[i] == 0)
			{
				if(r - sum - 1 >= l)
					ans[i] = 1;
			}
			else
				sum += opMi(i);
		}
		sum = 0;
		for(i=0; i<ansCnt; i++)
			if(ans[i])
				sum += opMi(i);
		cout<<sum<<endl;
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值