ARMERIA

Rubyと競技プログラミングの話 AtCoderやCodeforcesの問題解説記事が多め。

Educational Codeforces Round 58 G. (Zero XOR Subset)-less

お題箱より。

Problem - G - Codeforces

問題概要

長さ  n の数列  a_{0}, ..., a_{n-1} が与えられる。これをいくつかの区間(連続部分列)に分割する。この分割は以下の条件を満たす必要がある。

  • 全ての要素はちょうど1つの区間に属する。
  • 区間のうち1つ以上のものをどのように選んでも、それらに含まれる全ての要素のXOR和が  0 になることがない。

このような分割が可能であれば最大いくつの区間に分割できるかを求めよ。不可能であれば -1 を出力せよ。

制約

  •  1 \le n \le 2\times 10^{5}
  •  0 \le a_{i} \le 10^{9}

解法

XOR演算を記号  \oplus で表すことにします。また「区間和」や「累積和」などの用語は、XORで計算したものを指すことにします。

区間和を考える上では先頭からの累積和を取るのが有用です。まず先頭からの累積和  S_{i} = a_{0} \oplus \cdots \oplus a_{i-1} を計算します。ただし  S_{0} = 0 とします。

ある分割方法が本問題の条件を満たすための必要十分条件を、この累積和を用いて表現することが目標です。例えば以下の図のようにインデックス  x, y, z で4つの連続部分列に区切った分割を考えます。

f:id:betrue12:20200617191156p:plain

このときそれぞれの区間和は  S_{x}, (S_{y} \oplus S_{x}), (S_{z} \oplus S_{y}), (S_{n} \oplus S_{z}) となります。このうち1つ以上を選んでXORを取ったものが  0 にならないことが本問題の条件で、これはまさに線形代数における「一次独立性」です。整数を2進数表記して  0, 1 の要素からなるベクトルと考え、足し算の代わりにXOR演算を用いる、いわゆる  \mathbb{F}_{2} 上の線形代数と呼ばれるものを考えます。

この一次独立性は、「ある要素を、別の要素にXORで足す」という操作をしても変わりません(参考)。この性質から、 S_{x}, (S_{y} \oplus S_{x}), (S_{z} \oplus S_{y}), (S_{n} \oplus S_{z}) が一次独立であることは  S_{x}, S_{y}, S_{z}, S_{n} が一次独立であることと同値です。

より一般的に言えば、インデックス  b_{1}, ..., b_{k} k+1 個の区間に区切るような分割が本問題の条件を満たすことと、  S_{b_{1}}, ..., S_{b_{k}}, S_{n} が一次独立であることは同値です。すなわちこの問題は「累積和  S_{1}, ..., S_{n} の中から、 S_{n} を必ず選び、さらに他の要素を0個以上選ぶ。それらは一次独立である必要がある。最大でいくつの要素を選べるか?」という問題に帰着できます。

これは、もし  S_{n} = 0 であればどのように選んでも一次独立にできないので -1 を出力。そうでない場合は掃き出し法によって一次独立な要素の最大個数(=それらを基底として張られる線形空間の次元数)を求めることができます。

先ほど帰着した問題に素直に従うと、 S_{n} をまず基底に加えて、それから他の要素を追加していく掃き出し法によって解くことができます。ただ実際は、次元数は基底の候補をどの順番で処理するかに依存しないので好きな順番で追加して大丈夫です。

ACコード

Submission #84060649 - Codeforces