ABC138-F:Coincidence

経験不足だった。

問題

F - Coincidence

解法

考察

 yxで割った余りがx \ \rm{xor}\  yに等しいという条件が謎なので,もうすこしわかりやすい条件に言い換えることを目指そう。まず,x \ \rm{xor}\  yがある自然数xで割った余りであるということから,x \ \rm{xor}\  y \lt xが成り立つ。この不等式が成り立つためには,yの2進法表記としての桁数は,xのそれ以下で必要がある。さらに,x\leq yであるから,結局yxの桁数は等しくなければいけない。x\leq yで両者の2進法表記で桁数が等しいとき,y\lt 2xが言える(2倍することは桁を1つ増やすことに相当すると考えると明らか)。つまり,yxで割ったときの商として考えられるものは1しかない。よって,以下の等式を満たすようなx,yの組を数えればよい。

y  = x + (x \ \rm{xor} \ y)

この条件からx,yの各桁について何が言えるかを考える。xi桁目が1yi桁目が0であるとすると,左辺の足し算において繰り上がりが起き,辻褄が合わなくなる(i+1桁目がどうなるかを確認してみるとよい)。逆にそれ以外のパターンでは各桁が他の桁に影響を与えずに,条件が満たされることがわかる。つまり,yxの桁数が等しく,y,xi桁目をy_i,x_iとしたとき,(y_i,x_i) = (1,1),(1,0),(0,0)が満たされているようなものを数えればよいことがわかった。このとき,x\leq yが自動的に保障されるので,以後xyの大小は気にしなくてよい。
 厄介なのはL\leq x \leq y \leq Rという条件だろう。ここは慣れていないと難しいと思うが,桁dpの考え方を応用する。つまり

  • dp_{i,j,k}:i桁目まで見たときに,yRより小さいことが確定しているかどうか(j)xLより大きいことが確定しているかどうか(k),を決めたときの条件を満たす(y,x)の組の場合の数

としてdpを行う。

dpの詳細と実装

 bool値を2つ状態としてもつので遷移がややこしく,あまり雑にやると実装が破滅する。落ちついて,「今の状態を固定したときに,何を決めれば次の状態が決まるか」を表にしてみよう。yxの各桁の関係は重要だが,yi桁目を決めたときにyについて次にどのような状態になるかはxの情報は関係がない。よって,yxについて別々に表が作れる。
 今i桁目までの答えがわかっていて,i-1桁目の答えを知りたいということにしよう。Ri桁目はRと同じで,i-1桁以下はすべて0である数をR_iとする。このとき,dpテーブルのjという添え字は,「今はy\lt R_iであるか?」ということを意味している。ここからi-1桁目での状態を決定するには,Ri-1桁目が何であるかということと,yi-1桁目を何にするかということを決めればよい。これらの関係を表にすると以下のようになる。

yi-1桁目 Ri-1桁目 i桁目での状態 i-1桁目での状態
1 0 y \lt R_i y' \lt R_{i-1}
1 0 y = R_i なし
1 1 y \lt R_i y' \lt R_{i-1}
1 1 y = R_i y' = R_{i-1}
0 0 y \lt R_i y' \lt R_{i-1}
0 0 y = R_i y' = R_{i-1}
0 1 y \lt R_i y' \lt R_{i-1}
0 1 y = R_i y' \lt R_{i-1}

この表を作ってしまえば,i桁目の状態とi-1桁目を何にするかをfor文で回し,表を見ながら次の状態を決めるのことができて,実装が簡潔になると(個人的には)思っている。xについても同様の表を示しておく。

xi-1桁目 Li-1桁目 i桁目での状態 i-1桁目での状態
1 0 L_i \lt x L_{i-1} \lt x'
1 0 L_i = x L_{i-1} \lt x'
1 1 L_i \lt x L_{i-1} \lt x'
1 1 L_i = x L_{i-1} = x'
0 0 L_i \lt x L_{i-1} \lt x'
0 0 L_i = x L_{i-1} = x'
0 1 L_i \lt x L_{i-1} \lt  x'
0 1 L_i = x なし

y,xの桁数が等しいという条件をどう組み込むかについて述べるのを忘れていた。最上位の桁として選ぶことができるのは,Rの最上位の桁からLの最上位の桁までである。従って,該当する桁ではdp_{i,j,k}に適切にj,kを選んで1を足しておけばよい(詳細は実装を参照)。これでdpの遷移が完成した。

実装

Submission #7021885 - AtCoder Beginner Contest 138

 次の状態はだいたいは不等号なので,デフォルトで不等号にしておいて,特定の場合だけ変えるという実装がわかりやすいと思う。
 ところでmodintは便利ですね。