なんかCodeforcesで似た問題やったことある。
http://community.topcoder.com/stat?c=problem_statement&pm=12623
問題
3つの非負整数A,B,Cが与えられる。0<=X<=Aかつ0<=Y<=Bとなる整数(X,Y)の組のうち、 X xor Y <= Cとなるものを答えよ。
解法
xorで処理する問題はbit単位でループ回すのが定番だよね。
ということで、A,B,Cについて上の桁から順次処理をしていく。
(i+1)桁までの処理が終わり、A,B,Cは(i+1)桁より上のビットが立ってないとする。
簡単にするために、A>Bという過程を置く。
A,B,Cのi桁目の値をAi,Bi,Ciと置き、A,B,Cのi桁目を消した状態で以下の場合分けをする。
- Ci==1の時
- この時、X^Yのi桁目を0にすれば、確実にX^Y
- Ai==Bi==1の時:
- XiとYiを両方0にすると、(i-1)桁目以下は何を選んでもX^Y
- XiとYiを両方1にした場合、(i-1)桁目以下は(0~A)、(0~B)の何を選んでも良いので、(A+1)*(B*1)通り
- Xiが0、Yiが1にする場合、Yの(i-1)桁目以下は(0~B)しか選べないが、Xは(0~(1<<(i-1))-1)のどれでも選べる。よって、各YについてXを(0~(1<<(i-1))-1)通りすべて選ぶと、C以下になるのは(C+1)通りになる。よって掛け合わせて(B+1)(C+1)個選べる。
- Xiが1、Yiが0にする場合上記の逆で(A+1)(C+1)個選べる。
- 以上の4つの値を足したらそれで終了、ループから抜ける。
- Ai==1、Bi==0の時:
- XiとYiに両方0を選ぶ場合、Xの(i-1)桁目以下は任意に選べるので(B+1)(1<<(i-1))通り。
- Xiが1、Yiに0を選ぶ場合、まだXi^Yi==Ciなので小さくできるかわからない。よってループを継続する。
- Ai==0、Bi==0の時:
- XとYに何を選んでもi桁目は0なので、(A+1)(B+1)通り。
- Ci==0の時
- この時、X^Yのi桁目を0にしてもまだX^Y
Cなので不可。 - Ai==Bi==1の時:
- XiとYiを両方0にすると、XとYの(i-1)桁目以下は任意に選べるので、Xを(1<<(i-1))通り全て選んだ時、YはX^Y<=Cとなるものが(C+1)通り選べる。よって(1<<(i-1))(C+1)通り。
- XiとYiを両方1にした場合、Xi^Yi==0なのでループを継続
- Ai==1、Bi==0の時:
- Yの(i-1)桁目以下は(0~B)しか選べないが、Xは(0~(1<<(i-1))-1)のどれでも選べる。よって、X^Y<=Cとなるのは(B+1)(C+1)個選べる。
- Ai==0、Bi==0の時:
- Xi^Yi==Ci二しかならないので、次のループに行く。
- この時、X^Yのi桁目を0にしてもまだX^Y
- 最終的にループを途中で抜けないとA==B==C==0になるので、最後に場合の数に1を加算。
上記場合分けをコードに起こすと下記のようになる。
class LittleElephantAndXor { public: long long getNumber(int A, int B, int C) { ll ret=0; int la,lb,lc,i; for(i=29;i>=0;i--) { if(A<B) swap(A,B); la=A&(1<<i),lb=B&(1<<i),lc=C&(1<<i); A-=la; B-=lb; C-=lc; if(lc) { if(lb) { ret += (1LL<<(2*i)); // la==0 && lb==0 ret += (B+1)*(ll)(C+1); // la==0 && lb==1 ret += (A+1)*(ll)(C+1); // la==1 && lb==0 ret += (A+1)*(ll)(B+1); // la==1 && lb==1 return ret; } else if(la) { ret += (1LL<<i)*(ll)(B+1); // la==0 && lb==0 // la==1 && lb==0 } else { ret += (A+1)*(ll)(B+1); // la==0 && lb==0 return ret; } } else { if(lb) { ret += (1LL<<i)*(C+1); //la==0 && lb==0 //la==1 && lb==1 } else if(la) { ret += (B+1)*(ll)(C+1); //la==0 && lb==0 return ret; } } } return ret + 1; } };
まとめ
場合分けが結構面倒。