题目
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;
}
幸甚至哉,歌以咏志。