間違っている部分やもっと簡単にできる部分などがあれば教えていただけると助かります。
この記事での記法など
XOR 演算子は で書く。
非負整数の(有限)集合 に対して全体の XOR を とも書く。
非負整数の(有限)集合 に対して (何個か選んで作れる XOR 全体の集合)と書く。
を 行列とみたときの基底(XOR 基底)の つを と書く。
の要素はワードサイズに収まることを仮定する。ワードサイズを と書く。
本題
[1] 作れる XOR の種類数
[2] XOR を作る方法の数
とする。
- のとき、
- のとき、
つまり、作れる XOR はどれも、作る方法の数が同じである。*1
No.2672 Subset Xor Sum - yukicoder
[3] 作れる XOR のうち、ある bit が立っているものの個数
その bit が立っている要素が にない場合、明らかに
そうでない場合、その bit が立っているものと立っていないものは半々である。つまり、 である( の bit 目を と表した)。
- 理由: から選ばれる要素のうち、 bit 目が立っているものの個数の偶奇で分かれる。 bit 目が立っている要素を つ固定し、それを選ぶ・選ばないを flip することで全単射が作れる。
[4] chmin で XOR 基底を求めるアルゴリズム(いわゆる noshi 基底)
xor の掃き出しすごい簡単に出来るんですね
— 熨斗袋 (@noshi91) 2019年11月30日
vector<int> basis;
for(int e : a){
for(int b : basis)
chmin(e, e ^ b);
if(e)
basis.push_back(e);
}
これで数列 a の基底が basis に入る
このアルゴリズムを実行すると、msb*2 が distinct な基底が得られる。行列の言葉でいうと、(行を降順ソートすれば)階段行列になる。 の要素 つを処理するのに 時間。
[5] 作れる XOR のうち 番目に小さいもの
[4] のアルゴリズムを少し変形すると、msb が distinct なだけでなく
- msb は他の要素で立っていない(すなわち、 ならば )
を満たす基底が得られる(具体的なアルゴリズムはこの節の一番下にある問題の解説を参照)。行列の言葉でいうと、(行を降順ソートすれば)簡約行列になる。基底を計算するパートの計算量は [4] と同じ。
この基底をソートする(昇順に とする)。すると、各 を選ぶか選ばないかを で表したものが の 進表記と対応する。すなわち、 の要素を昇順に とすると、 のとき となる。クエリ 時間。
verify に使える・より詳細な解説が書いてある:G - Partial Xor Enumeration
[6] 作れる XOR のうち 以下で最大のもの(lower_bound や upper_bound のようなもの)
単純な二分探索で自明に 時間になるが、もっと高速になる。
[5] の基底をとる。基底を降順に見ていき、「選んでも を超えないなら選ぶ」ことを繰り返す。クエリ 時間。
[7] 奇数個・偶数個選んで作れる XOR
今あるどの bit よりも上の余っている bit を つ選び、全部の値で立てておく。その bit が なら偶数個、 なら奇数個選んだといえる。 を昇順に並べたとき、 番目はこの bit が に、 番目はこの bit が になる([3] の事実も参照)。
(偶数個選んで作れる XOR の集合)と書く。 に対して とすると、 となる。
No.803 Very Limited Xor Subset - yukicoder
ARC のネタバレ注意
E - Rearrange and Adjacent XOR
[8] 作れる XOR のうち、 との XOR が 番目に小さいもの
今あるどの bit よりも上の余っている bit を つ選び、 のその bit を立てたものを とする。 を考えると、 のうち先程新たに立てた bit が立っているもの、すなわち昇順で 番目のものが対応物([3] の事実も参照)。
最大だけなら、後で扱う問題の解説 に書いてある方法で求まる:[4] または [5] の基底を降順に見て、 を繰り返す。
任意の 番目で同様のことができるかは不明。
[9] 作れる XOR のうち、 との XOR が 以下で最大のもの
[8] 1. と [6] を組み合わせるとできる。
例題
非負整数列 と が与えられる。 を のもとで選べるとき、 の最大値を求めよ(あるいは、そのような選び方はないと判定せよ)。
(記号をこの記事用に変えています)
いったん を許して考える。
、 とすると、求めるものは
- の 未満の要素のうち下 bit が最大のもの
となる。 未満となる境界を [6] の方法で求める。 番目が 未満で最大であるとすると、条件を満たす選び方に対応する 進数が、区間 になっている。つまり、境界を求めるのに使った [5] の基底の下 bit を取り直して とすると、求めるものは
- を、対応する 進数が区間 にあるように選ぶときの の最大値
となる。(ここで であることが計算量的に効いてくる。)(ここで先程許した を考える。 以外で条件を満たせないのは、 で、 に対応する が の自明な 通りしかない場合。[2] より、 かどうかで判定できる。)
から、対応する 進数が区間 にある選び方をしたとき、選んだ整数と の総 XOR の最大値 とする。答は である。 番目を選ぶかどうかで場合分けして再帰する。
のとき
- 番目を選んでしまうと より大きくなるので選べない。 へ。
のとき
やっていることは公式解説と多分同じ(再帰をループで書いているだけ)。計算量も同じく になる。
*1:教えていただきありがとうございます https://twitter.com/Sophia_maki/status/1766876738024300696