tekiheiの日記

競技プログラミングについて書きます

Bitwise ORの最大化・最小化

以下の問題を解いていたときに考えたことです。

atcoder.jp

この問題はbitwise orとしてありうるものを数える問題ですが、今回は最小値と最大値を求めることを考えます。

問題

整数 A,Bが与えられます。 A以上 B以下の整数から1個以上選んで、それらのbitwise orを取ってできる整数としてありうるもののうち、最大値と最小値を求めてください。

制約
  •  1 \le A \le B \lt 2^{60} (元の問題と同じ制約にしています)
考察

 A = Bのときは Aしか作れないのでどちらも Aです。以下では A \lt Bの場合を考えます。

最小値について
bitwise ORを取ることによって数が小さくなることはないので、 1個だけ選ぶ場合を考えれば良いです。この場合の最小値は明らかに Aです。

最大値について
bitwise ORを取ることによって数が小さくなることはないので、答えは Aから Bまでの全ての数のbitwise ORです。これを求めることを考えます。 A Bが上位 k桁まで一致していたとします。そうすると A以上 B以下の全ての数の上位 k桁もこれに等しいので、答えの上位 k桁はこれと等しいです。
f:id:tekihei:20200202142548j:plain

残りの桁はどうなっているでしょうか。実は(?)残りの桁は全て 1になっています。なぜなら Bの上から k-1番目の桁は 1で、さらに A Bの間に k-2桁以下の桁が全て 1となるような数が存在するからです。
f:id:tekihei:20200202142620j:plain

実装
#include<bits/stdc++.h>
using namespace std;

using lint=long long;

int main()
{
   lint A,B; cin>>A>>B;
   if(A==B){ cout<<A<<' '<<B<<endl; return 0; }

   lint mn=A,mx=B;
   const int LOG=60;
   for(int i=LOG-1;i>=0;i--){
      if((A>>i&1)!=(B>>i&1)){
         for(int j=i-1;j>=0;j--) mx|=1LL<<j; 
         break;
      }
   }
   cout<<mn<<' '<<mx<<endl;
   return 0;
}
感想

どちらもbitwise ORを取って数が小さくなることはないという事実から分かるのが面白いと思いました。元の問題ではbitwise ORの最小値と最大値を求めることを何度かする必要があるので、一番小さい数が最小値、全部のORが最大値になるということが分かっていれば少し見通しが良くなるかなと思いました。