Action disabled: index

パナソニックプログラミングコンテスト2021(AtCoder Beginner Contest 231) F,H問題メモ

パナソニックプログラミングコンテスト2021(AtCoder Beginner Contest 231)

GとH、解法に使う技術はそれなりにわかりやすくても、応用のさせ方がなかなか高度。
(逆に言うと知識の有無が出やすかった?)

F - Jealous Two

問題

  • NN 種類のプレゼント候補があり、太郎と次郎に1個ずつ渡す
    • ii 番目の候補をもらった時の嬉しさは、太郎は AiAi、次郎は BiBi
    • 2人に同じ種類のプレゼントを渡してもよい
  • 太郎と次郎の少なくとも一方にとって、「相手がもらったプレゼントの自分にとっての嬉しさ」が、「自分がもらったプレゼントの自分にとっての嬉しさ」より大きいと喧嘩になる
  • プレゼントの渡し方は N2N2 通りあるが、喧嘩にならないようなものの個数を求めよ
  • 1N2×1051N2×105
  • 0Ai,Bi1090Ai,Bi109

解法

問題文が少しややこしいが、太郎に ii、次郎に jj を渡すとして、 喧嘩が発生しないのは以下の条件に言い換えられる。

  • (AiAj)AND(BiBj)(AiAj)AND(BiBj)

(Ai,Bi)(Ai,Bi) を二次元座標と捉えると、条件を満たす位置関係が把握しやすくなる(気がする)。

サンプル1

3 |      x
2 |          x
1 |  x
0 +------------
    50  100 150

ある点を太郎に渡す ii と固定した場合、そこより↖にある点が、jj として条件を満たす。

3 |      x   |
2 | _________x     (150, 2) を i とすると
1 |  x             j はラインより↖の範囲にある点。
0 +------------    (ただし境界を含む)
    50  100 150

これは、点全体を AiAi 基準でソートするとよい。

AiAi の小さい方からFenwick Treeなどのデータ構造に登録していくと、 「今まで登録した中でBiBi 以上の点の個数」を数えればよいことになり、 これは O(logN)O(logN) で高速に求められる。

AiAi が同じ点については、境界も含まれるので 今回は BiBi の大きい方から先にデータ構造に登録されていなければならない。
最初のソート順に気をつける。

また、Ai,BiAi,Bi とも全く同じ点が kk 個あったとしたら、 それらはどう渡してもよいので、k2k2 個の渡し方が存在する。

Ai,BiAi,Bi の一方でも異なれば、条件を満たしつつ i,ji,j を交換することはできないが、 全く同じであれば交換も可能なので、そこを数え忘れないようにする。

Python3

H - Minimum Coloring

問題

  • H×WH×W のグリッドに、NN 個の白い駒が置かれている
  • ii(Ai,Bi)(Ai,Bi) に置かれていて、コスト CiCi で黒い駒に変更できる
  • 全ての列・全ての行に黒い駒が1個以上ある状態にしたい
  • 必要なコストの和の最小値を求めよ
  • 1H,W,N1031H,W,N103
  • 1Ci1091Ci109
  • 最初、白い駒は全ての列・全ての行に1個以上ある

解法

最小流量制約付き最小費用流。

各行各列にちょうど1個の場合

今回の問題設定を少し簡単にすると、典型っぽい問題となる。

行と列の数が同じで、全ての行・列に「ちょうど」1個の黒い駒がある状態にするのは、 過去に類題があったと思うが、単純な最小費用流で解ける。

まず、行を表す HH 個の頂点、列を表す WW 個の頂点、超頂点 S,TS,T を用意する。

  • 駒が (Ai,Bi)(Ai,Bi) にあったら、AiBiAiBi に容量 11、コスト CiCi の辺を張る
  • SS から各行頂点に容量 11、コスト 00 の辺を張る
  • 各列頂点から TT に容量 11、コスト 00 の辺を張る

以上の辺を張ったら、STST に流量 HH を流す最小コストを求めれば答え。

今回の問題

1つの行・列に何個でも置いてよい(その方がコストが安いなら)。

上記の辺の張り方で、ST の容量上限が 1 ではなくなる。
また、最終的に ST にいくら流せば良いか(駒を何個置くのが最適か)もわからない。

それでもまぁ、容量上限は INF とするとして、 流量もまぁ多くとも H+W 流せば十分で残りを流すためのバイパスを作るとして、 以下のような最小費用流グラフ(仮)が作れる。

             行    列
               ,_--③ -,               S→行: 容量INF,コスト0
         _,- ①         \             行→列: 容量1  ,コストCi
H+W → ⓢ      `---④ ---ⓣ → H+W    列→T : 容量INF,コスト0
       |`--- ②----'    /|
       |       `---⑤ -' |
       `-----------------'          ←バイパス(容量INF,コスト0)

ただしこのままだと、当然コスト 0 のバイパスに全部流れてしまう。

S」「T」の各辺には、それぞれに少なくとも 1 の流量を流さなければならない。

これを実現するのが「最小流量制約付き最小費用流」で、 少しグラフの作り方を変えると、単純な最小費用流に変換できる。

まず、前提として

  • SとTだけでなく、各頂点に水の過不足 dv が決まっている
    • 正なら余っている(始点となり得る)
    • 負なら不足している(終点となり得る)
    • 全体を通しての過不足の総和は 0

というように考え方を拡張する。
この考えのもとでは、上記のグラフは dS=H+W,dT=(H+W)、他は dv=0 と表現できる。

この上で、流したい辺にはあらかじめ流したように dv を変更する。
つまり、

  • S に1流す
    • S の余剰が1減る(合計 H 減って dS=W となる)
    • 行の余剰が1増える
  • T に1流す
    • T の不足が1減る(合計 W 減って dT=H となる)
    • 列の不足が1増える

最後に、超超頂点1)P,Q として、

  • dv>0(正)の頂点には、Pv の容量 dv、コスト 0 の辺を張る
  • dv<0(負)の頂点には、vQ の容量 |dv|、コスト 0 の辺を張る

とし、PQ に、P から出ている辺の容量合計だけ流してやれば、答えとなる。

Python3

1)
語感が前前前世みたい
programming_algorithm/contest_history/atcoder/2021/1211_abc231.txt · 最終更新: by ikatakos
CC Attribution 4.0 International
Driven by DokuWiki Recent changes RSS feed Valid CSS Valid XHTML 1.0