[HDU5969]最大的位或

题目

B君和G君聊天的时候想到了如下的问题。
给定自然数(l)(r) ,选取(2)个整数(x,y)满足(l leq x leq y leq r),使得(x|y)最大。
其中(|)表示按位或,即(C,C++,Java)中的(|)运算。
Input
包含至多(10001)组测试数据。
第一行有一个正整数,表示数据的组数。
接下来每一行表示一组数据,包含两个整数(l,r)
保证(0 leq l leq r leq 1018)
Output
对于每组数据输出一行,表示最大的位或。
Sample Input
5
1 10
0 1
1023 1024
233 322
1000000000000000000 1000000000000000000
Sample Output
15
1
2047
511
1000000000000000000

解说

题意极其容易理解。
日常习惯先写个暴力试试,意料之中的(T)了。没办法,毕竟每次询问都是(O(n^2))
接下来我思路断了,于是瞎试,把(l)(r)每个数挨个位或一遍,结果所有样例都对上了。于是我开始思考这会不会就是正解?好像确实是呢,位或是只要两个数同一位有一个是(1)那么结果就是(1),那么我全位或一遍就相当于找了这两个数之间都有哪些位出现了(1)。虽然题目问的是找两个数,但稍微想一下就可以发现在这两个数之间是肯定可以找到把出现(1)的位都涵盖的两个数的。现在的单次时间效率是(O(n))
(O(n))的时间效率还能过不了?天理难容!交……(T)了。
看来要么是还有常数级效率的方法要么就是(O(logn))的方法。常数级不太可能,那么就只能试着把刚才的思想改成(O(logn))的方法,那肯定用到位运算。
和刚才做法等价的做法就是在(r)的范围内枚举每一位的(1),这样就相当于刚才的找(1)操作了。
终于(A)了。

代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;//记得开long long
ll read(){
   ll s=0,w=1;
   char ch=getchar();
   while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
   while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar();
   return s*w;
} 
int main(){
    int t;
    scanf("%d",&t);
    while(t--){
        ll l,r,tool=1,move=0;//tool就是枚举用的,move表示现在在第几位
        l=read(); r=read();
        while((l|(tool<<move))<=r){
        	l|=(tool<<move);
        	move++;//左移
		}
        printf("%lld
",l|r);
    }
    return 0;
}

幸甚至哉,歌以咏志。

原文地址:https://www.cnblogs.com/DarthVictor/p/12833327.html