B君和G君聊天的时候想到了如下的问题。
给定自然数l和r ,选取2个整数x,y满足l <= x <= y <= r ,使得x|y最大。
其中|表示按位或,即C、 C++、 Java中的|运算。
给定自然数l和r ,选取2个整数x,y满足l <= x <= y <= r ,使得x|y最大。
其中|表示按位或,即C、 C++、 Java中的|运算。
第一行有一个正整数,表示数据的组数。
接下来每一行表示一组数据,包含两个整数l,r。
保证 0 <= l <= r <= 1018 。
5 1 10 0 1 1023 1024 233 322 1000000000000000000 1000000000000000000
15 1 2047 5111000000000000000000
中文题目,很简单的题意。
题目分析:因为数据偏大,很显然用暴力会超时,果不其然我超时了一发,以为这是比赛场上的签到题可以无
做,没想到还是太年轻。然后来分析,因为给你l和r,所以位或的值肯定是可以用r和l到r的一个数表示的,那
现在我们考虑,先把l和r都换成二进制,然后从高位向低位遍历,并且l和r的位数要一致,不一致要用0补齐,
一旦有他们在同一位时l为0,r为1,立马停止,将此位后面的r全部补为1,所得到的值就是答案。
#include <cstdio> #include <cstring> #include <iostream> using namespace std; long long pow(long long a,long long b){ for(int i=2;i<=b;i++){ a=a*2; } if(b == 0) return 1; return a; } int main(){ long long l,r; int n; long long ans=0; scanf("%d",&n); int a[1100],b[1100],c[1100]; while(n--){ scanf("%lld%lld",&l,&r); int i=1,j=0; ans=0; long long ac=l,bc=r; memset(a,0,sizeof(a)); memset(b,0,sizeof(b)); memset(c,0,sizeof(c)); while(l){ a[i++]=l%2; l=l/2; } for(j=1;j<i;j++) swap(a[i-j],b[j]);//对l十进制变为二进制,用b存储 long long zz,z=i; zz=z; long long p; i=1;j=1; memset(a,0,sizeof(a)); while(r){ c[i++]=r%2; r=r/2; } for(j=1;j<i;j++) swap(c[i-j],a[j]); p=i; //对r十进制变为二进制,用a存储 memset(c,0,sizeof(c)); if(p>=zz){ for(int m=1;m<zz;m++,z--) c[p-z+1]=b[m]; }//保证c数组和b数组的位数相同 if(ac == bc ) printf("%lld\n",(ac|bc));//如果两数相等,则直接输出 else{ for(int k=1;k<p;k++){ if(a[k]==1 && c[k]==0){ for(int q=k;q<p;q++) a[q]=1; break; } } for(int i=1;i<p;i++) ans+=a[i]*pow(2,p-i-1);//二进制变十进制 printf("%lld\n",ans); } } return 0; }