−目次
パナソニックプログラミングコンテスト2021(AtCoder Beginner Contest 231) F,H問題メモ
パナソニックプログラミングコンテスト2021(AtCoder Beginner Contest 231)
GとH、解法に使う技術はそれなりにわかりやすくても、応用のさせ方がなかなか高度。
(逆に言うと知識の有無が出やすかった?)
F - Jealous Two
問題
- NN 種類のプレゼント候補があり、太郎と次郎に1個ずつ渡す
- ii 番目の候補をもらった時の嬉しさは、太郎は AiAi、次郎は BiBi
- 2人に同じ種類のプレゼントを渡してもよい
- 太郎と次郎の少なくとも一方にとって、「相手がもらったプレゼントの自分にとっての嬉しさ」が、「自分がもらったプレゼントの自分にとっての嬉しさ」より大きいと喧嘩になる
- プレゼントの渡し方は N2N2 通りあるが、喧嘩にならないようなものの個数を求めよ
- 1≤N≤2×1051≤N≤2×105
- 0≤Ai,Bi≤1090≤Ai,Bi≤109
解法
問題文が少しややこしいが、太郎に ii、次郎に jj を渡すとして、 喧嘩が発生しないのは以下の条件に言い換えられる。
- (Ai≥Aj)AND(Bi≤Bj)(Ai≥Aj)AND(Bi≤Bj)
(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 を交換することはできないが、 全く同じであれば交換も可能なので、そこを数え忘れないようにする。
H - Minimum Coloring
問題
- H×WH×W のグリッドに、NN 個の白い駒が置かれている
- 駒 ii は (Ai,Bi)(Ai,Bi) に置かれていて、コスト CiCi で黒い駒に変更できる
- 全ての列・全ての行に黒い駒が1個以上ある状態にしたい
- 必要なコストの和の最小値を求めよ
- 1≤H,W,N≤1031≤H,W,N≤103
- 1≤Ci≤1091≤Ci≤109
- 最初、白い駒は全ての列・全ての行に1個以上ある
解法
最小流量制約付き最小費用流。
各行各列にちょうど1個の場合
今回の問題設定を少し簡単にすると、典型っぽい問題となる。
行と列の数が同じで、全ての行・列に「ちょうど」1個の黒い駒がある状態にするのは、 過去に類題があったと思うが、単純な最小費用流で解ける。
まず、行を表す HH 個の頂点、列を表す WW 個の頂点、超頂点 S,TS,T を用意する。
- 駒が (Ai,Bi)(Ai,Bi) にあったら、Ai→BiAi→Bi に容量 11、コスト CiCi の辺を張る
- SS から各行頂点に容量 11、コスト 00 の辺を張る
- 各列頂点から TT に容量 11、コスト 00 の辺を張る
以上の辺を張ったら、S→TS→T に流量 HH を流す最小コストを求めれば答え。
今回の問題
1つの行・列に何個でも置いてよい(その方がコストが安いなら)。
上記の辺の張り方で、S→行 と 列→T の容量上限が 1 ではなくなる。
また、最終的に S→T にいくら流せば良いか(駒を何個置くのが最適か)もわからない。
それでもまぁ、容量上限は 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(正)の頂点には、P→v の容量 dv、コスト 0 の辺を張る
- dv<0(負)の頂点には、v→Q の容量 |dv|、コスト 0 の辺を張る
とし、P→Q に、P から出ている辺の容量合計だけ流してやれば、答えとなる。

