−目次
東京海上日動プログラミングコンテスト2023(AtCoder Beginner Contest 304)G,Ex問題メモ
東京海上日動プログラミングコンテスト2023(AtCoder Beginner Contest 304)
ジャッジが詰まってUnrated。サーバーがんばれー。
G - Max of Medians
問題
- 2N 個の非負整数 A1,...,A2N がある
- 2個ずつ組み合わせてbitwise-XORし、N 個の非負整数を作ることを考える
- 中央値として考えられる最大値を求めよ
- 中央値: 昇順に並べて ⌊N2⌋+1 番目の要素
- 1≤N≤105
- 0≤Ai<230
解法
中央値として達成できる最大値、みたいな問題は、答えを二分探索して、 「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 以上のペアを数えるには、
- k の d 桁目が '1'
- 次の桁からは、Sd0 と Sd1 から1つずつ選んでペアにすることが必須
- その前提のもとで、k 以上のペアを最大化
- k の d 桁目が '0'
- min 個のペアは k 以上確定
- 残り(S_{d0},S_{d1} のうち要素数が多い方)の中で、次の桁以降で k 以上となるペアを最大化
- ただし、あくまで(0,1)ペアを取った残りを組み合わせる前提なので、上限は \frac{要素数の差分}{2} 以下
なので、徐々に「これとこれの中から1個ずつ取らないといけない」 「この中同士でペアにしないといけない」制約が細分化されていく。
上から x 桁を処理した後の制約内のペアは、どのように組み合わせてもXORの結果の上から x 桁が全て同じになる。
また、k 以上・未満が確定したものは探索の必要は無いので、実質的に調べる中では、上から x 桁は k とも同じになる。
2桁目以降
2桁目以降は、既に2つに分けられ「A と B から1つずつ選んでね」という制約付きで上から降ってくることがある。
A,B それぞれで、着目中の桁 d の0,1で4グループに分けて、A_0,A_1,B_0,B_1 とする。
- k の d 桁目が '1'
- (A_0,B_1) または (A_1,B_0) でペアにしないといけない。それぞれで最大化した結果を求めて合計
- k の d 桁目が '0'
- (A_0,B_1) および (A_1,B_0) で優先的にペアを作る
- A_0,B_0 がともに余るとき、この2つから1つずつ選んで k 以上を最大化
- A_1,B_1 がともに余るとき、この2つから1つずつ選んで k 以上を最大化
- ※ただし上限は余る個数
とすればよい。
再帰関数
- f(A, k, d)=A から2個ずつ取って k 以上のペアを最大化した時の個数、d 桁目に着目
- g(A, B, k, d)=A,B から1個ずつ取って k 以上のペアを最大化した時の個数、d 桁目に着目
の2つの関数を定義しておくと、上記の遷移を実現できる。
f(入力として与えられるA, 二分探索で試行するk, 29) が求めるもの。
再帰の末端は、
- 最後の桁まで行き着いたら(d=-1)
- f(A,k,d) なら \left \lfloor \dfrac{|A|}{2} \right \rfloor
- g(A,B,k,d) なら \min(|A|,|B|)
- 途中で打ち切れる条件
- f(A,k,d)なら |A| \lt 2 のとき、結果は0
- g(A,B,k,d) なら \min(|A|,|B|)=0 のとき、結果は0
貪欲でよい証明
(0,1)ペアは優先的に作ってよいとしたが、本当か?
- 敢えて(0,1)ペアを最大まで作らず、(0,0)、(1,1)のペアを多くすることで、k 以上にできる個数が多くなることはあるか?
(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以上がこっちの方が多くなることはある?
ない。
- k の d 桁目が'1'のとき
- (0,1)ペアを多くしてXORも'1'にしないと k 未満確定なので、(0,0)(1,1)を増やす意味は無い
- k の d 桁目が'0'のとき
- (0,0)ペア、(1,1)ペアいずれかの中に k 以上にできないものがあるなら、それをバラして(0,1)として組み合わせた方がよい
- (0,0):x,(1,1):o → (0,1):o,(0,1):o
- 全て k 以上にできるとしても、(0,1)として組み合わせても損しない
ので、(0,1)ペアは優先的に組み合わせることにして問題ない。
計算量
1回の判定問題で、1要素当たり最大 O(\log{A_{max}}) 回の再帰で評価されるので、O(N\log{A_{max}})
二分探索するので、O(N(\log{A_{max}})^2)
Ex - Constrained Topological Sort
問題
- N 頂点 M 辺の有向グラフがある
- (1,2,...,N) の順列 P=(P_1,...,P_N) として、頂点 i に値 P_i を割り当てる
- P は以下の条件を全て満たさないといけない
- u→v に辺があるとき、P_u \lt P_v
- 各頂点に割り当て可能な値の範囲制約があり、頂点 i に対して L_i \le P_i \le R_i を満たす
- 割り当て可能か判定し、可能なら P の一例を示せ
- 2 \le N \le 2 \times 10^5
- 0 \le M \le 4 \times 10^5
解法
トポロジカルソートと、区間スケジューリング問題みたいな R_i の昇順に貪欲的に決めていけるやつ(呼び方忘れた)の複合。
問題は以下のように言い換えられる。
- グラフの辺に従ってトポロジカルソートし、頂点番号を順に記録する
- 頂点 i は、ソート結果で L_i~R_i 番目の中にないといけない
- 矛盾無くソートできたなら、a 番目に頂点 b が記録されたとして P_b=a としたものが答え
トポロジカルソートのアルゴリズムは、ソート結果を記録する空配列 T を用意し、流入辺が無くなった頂点からキューに入れ、キューから取り出された順に T に追加しグラフから削除する、というものである。
キューに複数の頂点が同時にあるとき、どれを取り出すかに自由度がある。
- キューに入っている頂点の内、制約が厳しいものを優先的に取り出す
- 頂点 i が取り出されたとき、L_i~R_i 番目の中にあるかチェックする
- まだ T の長さが L_i-1 に達していないなら、頂点 i は(流入辺が無くても)キューには入れない
- キューに入っている頂点は、L_i の条件は必ず満たす状態にしておく
- 入れられない頂点は、「T の長さが l になったら入れる」みたいなのに別途記録して後で入れる
という改変を加えることで、条件を満たすソート結果の一例を作れる。(または破綻する)
キューから取り出す優先順位である「制約が厳しい」とは、 その頂点の R_i だけでなく、そこから辺で繋がる頂点の R_i にも影響される。
①→② この時、①は 4 番目までに置く必要がある Ri 10 5
よって、まず最初に後ろからトポロジカルソート的なことをし、各頂点の実質的な制約 R'_i を求めておく。
- R'_i = \min(R_i, R'_{j1}-1, R'_{j2}-1, ...)
- ただし i から直接辺で繋がる頂点を j_1,j_2,... とする
あとは、先ほどの改変を加えてトポロジカルソートし、N 個の頂点を矛盾無く並べられるかチェックすればよい。