人間だけど競プロやる

解けなかった問題を理解できたら記事を投稿します。日本語の解説が見つからなかったもの中心。

Codeforces Good Bye 2020 E. Apollo versus Pan

Good Bye 2020 E. Apollo versus Pan

まず、

  • シグマをみたら式変形
  • ビット演算は桁ごとに見る

のが基本なのでやってみる

\displaystyle{\sum^{N}_{i} \sum^{N}_{j} \sum^{N}_{k} {(x_i \& x_j) \times (x_j | x_k )}}

(x_i \& x_j)kに依存しないのでシグマの外に出す
\displaystyle{\sum^{N}_{i} \sum^{N}_{j}  {(x_i \& x_j)} \times \sum^{N}_{k} { (x_j | x_k )}}

(x_i \& x_j)(x_j | x_k)の両者がjに依存するので、シグマの順番を入れ替える
\displaystyle{\sum^{N}_{j} \sum^{N}_{i}  {(x_i \& x_j)} \times \sum^{N}_{k} { (x_j | x_k )}}
\displaystyle{\sum^{N}_{j} \sum^{N}_{i}  {(x_i \& x_j)} \times \sum^{N}_{k} { (x_j | x_k )}}
\displaystyle{\sum^{N}_{j} \biggl( \sum^{N}_{i}  {(x_i \& x_j)} \times \sum^{N}_{k} { (x_j | x_k )} \biggr)}

これはプログラムで書くと、

let mut ans = 0;
for i in 0..N {
    let mut x = 0;
    let mut y = 0;
    for j in 0..N {
        x += a[i] & a[j];
        y += a[i] | a[j];
    }
    ans += x * y;
}

となる
このままではO(N^2)となり間に合わないので、forの中のビット演算を桁ごとにみることで計算量を減らしたい。
この式(プログラム)を解釈すると、a_iを順番にみていって、各a_iにたいして、a_iからa_NまでとANDとORをとって足したものを掛け合わせたものを、足していっている。
つまり、a_iをみたときに、a_iからa_NまでとAND(OR)をとった和が高速に求まれば良い。

これはよくある設定で、
2進数で各桁でみていってとき、a_iのd桁目が「1」なら、a_iからa_Nでの各数のd桁目が「1」なら「AND」をとった結果は「1」となり、「0」なら「AND」をとった結果は「0」となる。
同様にa_iのd桁目が「1」なら、a_iからa_Nでの各数のd桁目が「1」「0」どちらであれ「OR」とった結果は1になる。a_iのd桁目が「0」なら、a_iからa_Nでの各数のd桁目が「1」なら「1」、「0」なら「0」となる。
d桁目が「1」になる場合がいくつあるのかがわかれば、それらの和は2^d \timesいくあるかで求めることが出来る。 (2進数でd桁目は2^k)
つまり、a_iからa_Nまでの各数にたいして、d桁目が「1」がいくつあるかの分布がわかれば高速に求めることが出来る。


modを自分で毎回とるとバグりがち&「1 << d」とかを32bitで処理しないように注意。
この方法でAC。