Quantcast
Channel: プログラミング
Viewing all articles
Browse latest Browse all 8395

ABC365 E - Xor Sigma Problem - JunKobayashi’s diary

$
0
0

↓問題のページ↓

E - Xor Sigma Problem

 

本質的には 公式解説をなぞっているだけだが、自分の実力が不足しすぎていてなかなか理解できなかったため結論までの詳細を書き残す。この手の問題は典型らしいので素早く処理できるようになりたい・・・。

まず、「bitごとの和を計算して得られる値が答えである」と言える理屈(当初ここが全く分からず困惑した)を確認していく。本問はXORが関与してくるが、一旦そのことは忘れて普通に10進数どうしを足すことを考えよう。

例えば「計算機を使わずに\(52413 + 87069\)の値を求めてください」と言われたとして、どうすればいいのか全く分からずに困り果てる人は未就学児以外いないだろう。普通に筆算なり算盤なりを利用して\(52413 + 87069 = 139482\)と簡単に分かるのだが、この過程をもう少し深堀りしていく。

\(52413 , 87069\)はどちらも10進数であるから、この2つの整数は\begin{align}  52413 &= 5 \times 10^4 + 2 \times 10^3 + 4 \times 10^2 + 1 \times 10^1 + 3 \times 10^0 \\ 87069 &= 8 \times 10^4 + 7 \times 10^3 + 0 \times 10^2 + 6 \times 10^1 + 9 \times 10^0 \end{align}と書き表すことができる。この2式を足し、右辺に関してはべき乗の表記を崩さないようにして同類項をまとめると、\begin{align}  139482 &= 13 \times 10^4 + 9 \times 10^3 + 4 \times 10^2 + 7 \times 10^1 + 12 \times 10^0  \tag{1} \end{align}となる。本来、10進法で表記された整数の各桁は\(0 \le m \le 9\)を満たす整数\(m\)にしかならないが、一旦その制約を忘れる。この時、\(a_i , b_i\)をそれぞれ「\(52413 , 87069\)の\(10^i\)の位に書かれている整数」と定義すると、\(139482 \)という整数は「\(10^i\)の位が\(a_i + b_i\)であるような10進数」と(やや強引に)解釈することができる。以上のように、10進数同士を足し算する場合、各桁の値どうしを足し合わせた値を機械的に対応する桁に書き並べていっても、式\((1)\)のように計算すれば最終的な結果を正しく求めることができるため、各桁について和を取っていき、適宜\(10\)のべき乗を掛けた値を次々と足していくことによって2つの整数の和を求めることができる。

では次に2進数どうしを足していく場合を考えよう。10進数で表記された3つの整数\(13, 10, 7\)の和を考える。それぞれを2進法で表記すると、\begin{align}  13 &= 1101_{(2)} \\ 10 &= 1010_{(2)} \\ 7 &= 0111_{(2)} \end{align}となる。これら3つの2進数も、当然筆算を行うことによって容易に和を求めることができ、その結果は\begin{align}  30 &=  11110_{(2)} \end{align}である。こちらについても先ほどの10進数の和と同様に考えていこう。まず、加算の対象となる3整数を単にbit列で表記するのではなく、\begin{align}  13 &= 1 \times 2^3 + 1 \times 2^2 + 0 \times 2^1 + 1 \times 2^0 \\ 10 &= 1 \times 2^3 + 0 \times 2^2 + 1 \times 2^1 + 0 \times 2^0 \\ 7 &= 0 \times 2^3 + 1 \times 2^2 + 1 \times 2^1 + 1 \times 2^0 \end{align}と表す。これら3式を足し、右辺は単に同類項をまとめるのみとすると、その結果は\begin{align}  30 &= 2 \times 2^3 + 2 \times 2^2 + 2 \times 2^1 + 2 \times 2^0 \tag{2} \end{align}となる。当然ではあるが、式\((2)\)の右辺を計算すれば左辺と一致することは容易に確かめられる。ここまでの結果から、「\(11110_{(2)}\)という2進数は、\(1101_{(2)} , 1010_{(2)} , 0111_{(2)}\)という3つの2進数について、各bitの値を単純に足した値を各桁とする2進数(無理矢理表記すれば\(2222_{(2)}\)のようになる)と等価である」という(同じく強引な)解釈ができる。以上のようなことは加算の対象となる値が何進数で表記されていようとも全く同様に考えられるので、本問で求めるべき値について、bitごとに(適宜\(2\)のべき乗を忘れずに掛けていきながら)各項の値を足していっても正しい結果となることが分かる。

それでは実際にbitごとに和を取っていくことにより、問題で与えられた数式の計算結果を求めていこう。まず、\(i \lt j\)を満たす\((i, j)\)に対してのみ、\begin{align} A_i \oplus A_{i + 1} \oplus \dotsc \oplus A_j \tag{3}\end{align}を求めて全て足し上げていくというのが問題で要求されていることだが、このままでは各区間の長さが必ず\(2\)以上でなければならなくなり扱いにくいので式を変形する。\(i = j\)の場合、式\((3)\)は単に\(A_i\)とできることを利用すると、\begin{align} \sum_{i=1}^{N-1}\sum_{j=i+1}^N (A_i \oplus A_{i+1}\oplus \ldots \oplus A_j) \\ = \sum_{i=1}^{N}\sum_{j=i}^N (A_i \oplus A_{i+1}\oplus \ldots \oplus A_j) - \sum_{i=1}^{N} A_i \tag{4} \end{align}というように変形することができる。式\((4)\)の第2項は容易に求められるので第1項について考えよう。制約を確認すると、\(N \le 2 \times 10^5\)であるから2重ループによる愚直な計算をしてしまうと当然TLEするので工夫して高速化する必要がある。そこで、累積XORを導入すると良い。\(B_0 = 0\)として、以下のように\(B_q\)の値を定義する。\begin{align} B_q &= A_1 \oplus A_2 \oplus  \dotsc \oplus A_{q - 1} \oplus A_q \\ ( &= B_{q - 1} \oplus A_q)\end{align}このように定義した\(B_q\)を利用すると、式\((4)\)の第1項は\begin{align} \sum_{i=1}^{N}\sum_{j=i}^N (B_{i - 1} \oplus B_j)  \tag{5} \end{align}と変形することができる。

※ここではXOR演算が持つ以下2つの性質を利用している。\begin{align} V \oplus V &= 0 \\ V \oplus 0 &= V \end{align}また、制約を確認すると、\(A_p \le 10^8\)であり、\(2^{30} - 1 \gt 10^8\)を満たすので、各\(A_p\)のためには多めに見積もっても30ビット用意しておけばよく、式\((5)\)の値を\(2^0\)の位、\(2^1\)の位、\(\dotsc\)、\(2^{28}\)の位、\(2^{29}\)の位について求めていき、それぞれを足していこう。

\(2^k\)の位による寄与の大きさを求めていく。この時、各\(A_p\)の値は\(k\)ビットだけ右シフトした値と\(1\)とのAND演算により、\(2^k\)の位のみが取り出されているものとする(つまりは\(A_p \in \{ 0, 1 \}\)が成立している)。この時、\(B_q \in \{ 0, 1 \}\)も当然成立しており、単なる1ビットのXOR演算であるから、\begin{equation}
  B_{i - 1} \oplus B_j =
  \begin{cases}
    0 & \text{if \(B_{i - 1} = B_j\)} \\
    1 & \text{if \(B_{i - 1} \ne B_j\)}
  \end{cases}
\end{equation}が成り立っている。\(B_{i - 1} \oplus B_j = 0\)であるような\((i-1, j)\)は和の結果に影響しないので、\(B_{i - 1} \oplus B_j = 1\)であるような\((i-1, j)\)がどれだけあるのかを考える。まず\(t = 1, 2, \dotsc , N\)について、定義通りに\(B_t\)を求めていくのと同時に、\(B_t = 0\)なる\(t\)を昇順に格納したリスト\(X\)、\(B_t = 1\)なる\(t\)を昇順に格納したリスト\(Y\)を作る(ただし\(X\)には最初から\(0\)を入れておく)。ここまでできたら、\(0 \le i-1 \le N-1 \land j \ge i \)であることに注意して何通りあるかを求めていく。\(X, Y\)併せて\(0, 1, 2, \dotsc , N-1 , N\)が各要素として一度ずつ現れることから、\(X, Y\)それぞれの要素数を\(M_X , M_Y\)として、\(X, Y\)それぞれから任意に取り出した1つの値を\(v_X , v_Y\)とすると、\(v_X , v_Y\)のうち小さい方を\(i-1\)、大きい方を\(j\)とすればよいので、\(B_{i - 1} \oplus B_j = 1\)であるような\((i-1, j)\)は\(M_X M_Y\)通り存在すると分かる。というわけで、\(2^k\)の位による寄与の大きさは\(M_X M_Y \times 2^k\)であると分かった。これを全ての\(k\)について求めて合計すれば、その値がまさに式\((5)\)の値、すなわち式\((4)\)の第1項の値となる。以上によってこの問題を解くことができた。計算量を雑に見積もると、\(N\)個の要素に対する線形探索を30ビット分行うこととなり、最悪で\begin{align} 30 \times 2 \times 10^5 = 6 \times 10^6 \end{align}くらいなので間に合う。


Viewing all articles
Browse latest Browse all 8395

Trending Articles