東京海上日動プログラミングコンテスト2023(AtCoder Beginner Contest 304)
ジャッジが詰まってUnrated。サーバーがんばれー。
中央値として達成できる最大値、みたいな問題は、答えを二分探索して、 「k 以上の値を半数(⌊N+12⌋)以上にできるか?」 の判定問題に落とし込む解法がよくある。
「XORが k 以上になるペアを最大何個作れるか?」を解く。
2進数で上の桁から再帰的に「k 以上にするためのペア」で分類していく。
Ai のうち、2進数で d 桁目が0のやつの多重集合を Sd0、1のやつを Sd1 とする。
Sd0,Sd1 から1つずつ取ったペア((0,1)ペア)のXORは d 桁目が1、
同じ中から2つ取ったペア((0,0),(1,1)ペア)は0になる。
この時、上の桁から貪欲に、その桁が'1'となるペアを最大化していってよい。(理由は後述)
d A 1011 Sd1 Sd0とSd1を優先的にペアにし、 1001______ (今回はSd0の方が多いので) 0111 Sd0 残ったSd0同士でペアにする 0101 0100 0010 0000 0000
k 以上のペアを数えるには、
なので、徐々に「これとこれの中から1個ずつ取らないといけない」 「この中同士でペアにしないといけない」制約が細分化されていく。
上から x 桁を処理した後の制約内のペアは、どのように組み合わせてもXORの結果の上から x 桁が全て同じになる。
また、k 以上・未満が確定したものは探索の必要は無いので、実質的に調べる中では、上から x 桁は k とも同じになる。
2桁目以降は、既に2つに分けられ「A と B から1つずつ選んでね」という制約付きで上から降ってくることがある。
A,B それぞれで、着目中の桁 d の0,1で4グループに分けて、A_0,A_1,B_0,B_1 とする。
とすればよい。
の2つの関数を定義しておくと、上記の遷移を実現できる。
f(入力として与えられるA, 二分探索で試行するk, 29) が求めるもの。
再帰の末端は、
(0,1)ペアは優先的に作ってよいとしたが、本当か?
(0,1)最大 その他の組み合わせ方例 Sd1 1???? x2 → 1???? (0,1) 0???? (1,1) Sd0 0???? x6 1???? (0,1) 0???? (0,0) 0???? (0,0) 0???? (0,0) 0???? (0,0) 0???? (0,0) ↑k以上がこっちの方が多くなることはある?
ない。
ので、(0,1)ペアは優先的に組み合わせることにして問題ない。
1回の判定問題で、1要素当たり最大 O(\log{A_{max}}) 回の再帰で評価されるので、O(N\log{A_{max}})
二分探索するので、O(N(\log{A_{max}})^2)
トポロジカルソートと、区間スケジューリング問題みたいな R_i の昇順に貪欲的に決めていけるやつ(呼び方忘れた)の複合。
問題は以下のように言い換えられる。
トポロジカルソートのアルゴリズムは、ソート結果を記録する空配列 T を用意し、流入辺が無くなった頂点からキューに入れ、キューから取り出された順に T に追加しグラフから削除する、というものである。
キューに複数の頂点が同時にあるとき、どれを取り出すかに自由度がある。
という改変を加えることで、条件を満たすソート結果の一例を作れる。(または破綻する)
キューから取り出す優先順位である「制約が厳しい」とは、 その頂点の R_i だけでなく、そこから辺で繋がる頂点の R_i にも影響される。
①→② この時、①は 4 番目までに置く必要がある Ri 10 5
よって、まず最初に後ろからトポロジカルソート的なことをし、各頂点の実質的な制約 R'_i を求めておく。
あとは、先ほどの改変を加えてトポロジカルソートし、N 個の頂点を矛盾無く並べられるかチェックすればよい。