AtCoder × Engineer Guild オンサイトコンテスト ~集結!高レート人材~予選(AtCoder Beginner Contest 424)F,G問題メモ

AtCoder × Engineer Guild オンサイトコンテスト ~集結!高レート人材~予選(AtCoder Beginner Contest 424)

コロナでぶっ倒れてたので参加できず、高レート人材の入口にも立てませんでした。

F - Adding Chords

問題文

  • 円周上に等間隔に $N$ 個の点があり、時計回りに $1,2,\dots,N$ と番号がついています。
  • 以下の形式のクエリが $Q$ 個与えられるので順に処理してください。
    • 点 $A_i$ と点 $B_i$ を結ぶ線分を書く。ただし、この線分が既に書かれている線分と交わる場合は書かない。
  • ここで、$2Q$ 個の整数 $A_1,\ldots,A_Q,B_1,\ldots,B_Q$ は相異なることが保証されます。
  • 各線分が書かれたかどうか答えてください。

制約

  • $2 \leq N \leq 10^6$
  • $1 \leq Q \leq 3\times 10^5$
  • $1 \leq A_i \lt B_i \leq N$
  • $2Q$ 個の整数 $A_1,\ldots,A_Q,B_1,\ldots,B_Q$ は相異なる
  • 入力は全て整数

解法

「円周上の線分が交わる」というのは、 「$1$ と $N$ の間で切り開いて線分を区間として表したとき、区間が交差する」と言い換えられる。

×
区間1       ,-----------,             元の円に戻したとき、
        1---|----|------|--|---N    →交差してしまう
区間2            `---------'

○
区間1       ,--------------,          包含関係にあるのは
        1---|----|------|--|---N    →交差しない
区間2            `------'

よって区間 $i$ が追加できるかどうかは、 「$A_i$ と $B_i$ の間に、片方の端のみが入っている追加済みの区間はあるか?」が知れればよい。

これは、セグメント木 + Zobrist Hash を使えば実装しやすい。
Zobrist Hash は集合を管理できるHashだが、言い換えれば「集合(区間)内にある同じ値は全て偶数個ずつ存在する」かどうかを、 (確率的ではあるが、十分に高い確率で)高速に調べられる。

Python3

G - Set list

問題文

  • $N$ 人のアイドルからなるグループと、$M$ 個の曲があります。
  • 高橋君は、これらのうちから何曲か($0$ 曲でも良い)を選びライブを行いたいと考えています。
    • 同じ曲は $1$ 回までしか選ぶことはできません。
    • アイドル $i$ $(1\leq i\leq N)$ は $A_i$ 曲まで踊ることができます。
    • ライブにおいて、曲 $j$ $(1\leq j\leq M)$ では $B_j$ 人のアイドルが踊る必要があり、(誰が踊ったかによらず)その曲の盛り上がりは $C_j$ です。
  • 高橋君は、それぞれのアイドルが踊れる回数を超えないように、ライブで演奏する曲およびそれぞれの曲で踊るアイドルを選びます。
  • 選んだ曲の盛り上がりの総和としてあり得る最大値を求めてください。

制約

  • $1\leq N\leq 100$
  • $1\leq M\leq 100$
  • $0\leq A_i\leq M$
  • $0\leq B_i\leq N$
  • $0\leq C_i\leq 10^9$
  • 入力はすべて整数

解法1

制約も小さく、何となくフローっぽさを感じるものの、いざフローで解こうとすると上手くいかない。
ある曲で利益 $C_j$ を得るためにはそこに $B_j$ を「流しきらないと」いけないが、 そのような線形でない条件はフローでは上手く扱えない(はず)。

ある曲を採用する曲集合に加えても良いかどうかを判定するのに、「$B_j$ の降順」に決めていくと良いことを利用する。

$k$ 曲を選び、それを $B_j$ が大きい順に $B_1,B_2,...,B_k$ と並べなおす。

任意の $1 \le k' \le k$ について、 $\displaystyle \sum_{i=1}^{N}\min(A_i,k') \ge \sum_{j=1}^{k'}B_j$ が成り立つ(★)ことが、 その $k$ 曲を全て踊るようなアイドルの割り当てが存在する必要十分条件である。

略証

以下のように、$A_i$ を横に数えて下から累積和を取って $C_k$ としたとき、 「採用した中で大きい方から $k$ 曲の $B_j$ の和は $C_k$ 以下」が各 $k=1,2,...,採用曲数$ で成り立てばよい。

                                Ck
  ↑Ai  □                    ┐29    大きい方から1曲の Bi の和は10以下
  |    □□□                ┐28    大きい方から2曲の Bi の和は19以下
  |    □□□□□□          ┐25    大きい方から3曲の Bi の和は25以下
  |    □□□□□□□□□    ┐19    ...
  |    □□□□□□□□□□  ┐10
  +--------------------------> 各アイドル

曲を $B_i$ 降順にソートした上で採用するかどうかを決めていくと、常にこれが成立している状態を保ってDPできる。

  • $\mathrm{DP}[i,j,k]:=i$ 曲目までのうち $j$ 曲を採用すると決めて、$B_i$ の和が $k$ である場合の最大盛り上がり

$C_j \lt k$ となるような $(j,k)$ には遷移できない。

計算量は $O(N^4)$ となる。

Python3

解法2

解法1の条件は天下り的な上に証明も大変だったが、フローを使うともう少し自然に導出できる。
フローは直接的に答えを求めるのではなく、曲集号に対して割り当て可能か?という判定問題に用い、 その考え方を元にしてDPするのは解法1と変わらない。

曲集合を固定すると、以下のようなグラフで最大流を流した時、 $\sum B_i$ だけ流れれば割り当て可能と言える。

       アイドル       曲
  ,--A1-> ○          ○-B1--,
  |           容量1の        v
(S)--A2-> ○ 完全二部 ○-B2->(T)
  |           グラフ         ^
  `--A3-> ○          ○-B3--'

最小カットに言い換えたとき、 「曲→(T)の間の辺(容量 $B_1~B_3$)を全て切るよりコストの安い切り方が、(S)~アイドル~曲 の間になければ割り当て可能」といえる。

仮に 曲→(T) 間の辺のうち、$Y$ 本を切らずに残すときの最小カットを確認する。
この時、他でカットを成立させるためには以下の切り方をするのが最も安くなる。

  • $A_i \le Y$ なら (S)→アイドル 間の容量 $A_i$ の辺
  • $A_i \gt Y$ なら アイドル→曲 のうち、曲→(T)間が切られてない曲への容量1の $Y$ 本の辺

つまり、$\displaystyle \sum_{i=1}^{N} \min(A_i,Y) \ge \sum_{切られてないj} B_j$ であることが、 「$Y$ 本を切らないと仮定しても最小カットが安くならない」条件になる。

切らない $Y$ 本の決め方はいろいろあるが、その全てで上記が成り立たないといけないので、 $B_j$ が大きい方から $Y$ 本を切らないようにしたもので成立するかどうかを確認すれば十分となる。

これが、$Y=1,2,...,|曲集号|$ の全てで成り立てば、割り当てが存在することが示せる。

すると、解法1の条件と同じになる。

解法3

フローでは直接的に答えを求めるのは無理だが、より複雑な設定を扱える混合整数計画問題、MIPソルバに解かせるのは可能。

リンクの流量 $x_i$ の他に、そこに流量いっぱいまで流すかの0-1変数 $y_i$ を導入する。

報酬は $\sum_i y_i C_i$ として表し、また流量には $x_i = y_i \times B_i$ という制約を付ける。

他の最小費用流を定式化する際の制約も課せば、ソルバにとかせられる形になる。

programming_algorithm/contest_history/atcoder/2025/0920_abc424.txt · 最終更新: by ikatakos
CC Attribution 4.0 International
Driven by DokuWiki Recent changes RSS feed Valid CSS Valid XHTML 1.0