AtCoder Beginner Contest 187 D,E,F問題メモ

AtCoder Beginner Contest 187

PC新調したら移行作業で正月がつぶれた。

いろんなアプリに依存してると、こういう時に苦労するね。(データ移行がきちんと考えられているアプリばかりではないので)

D - Choose Me

問題

  • AA 氏と BB 氏の2人から1人を選ぶ選挙を行う
  • 選挙区は NN
  • ii 番目の選挙区の有権者は全員 AA 派、BB 派のいずれかであり、それぞれ Ai,BiAi,Bi 人いる
  • 選挙は全ての選挙区からの総得票数の多い方が勝つ
  • 何もしないと、AA 派はそのまま AA 氏に投票し、BB 派はどこにも投票しない
  • BB 氏は選挙区で演説を行うことで、その選挙区の全ての人を BB 氏に投票させることができる
  • AA 氏は何もしない
  • BB 氏が勝つために最低限演説を行う必要のある選挙区の数を求めよ
  • 1N2×1051N2×105

解法

何もしない BB 派にはなるべく投票に行って欲しいし、そのままでは AA 氏に投票してしまう AA 派にはなるべく寝返って欲しい。

増やしたい指標が2つあるのを、上手く考えて統合する。

得票数の差分(BABA)に注目すると、

  • 何もしないと、AiAi の総和だけ AA 氏が票を得て、BB 氏は0票。よって差分は iAiiAi
  • 選挙区 ii で演説すると、AA 氏が AiAi 票減り、BB 氏は Ai+BiAi+Bi 票増え、差分は 2Ai+Bi2Ai+Bi 縮まる
  • 最終的に差分が正になれば勝ち

なので、2Ai+Bi2Ai+Bi の大きい順に演説するのが得で、その累積和が iAiiAi を超えるまで回る必要があるとわかる。

Python3

E - Through Path

問題

  • NN 頂点の木が与えられる
    • ii 番目の辺は ai,biai,bi を結ぶ
    • 頂点には数字が書かれていて、はじめ、全て0である
  • その後、クエリが QQ 個与えられる
  • ii 番目のクエリでは、ti,ei,xiti,ei,xi が指定される
  • クエリ
    • ti=1ti=1 なら、辺 eiei について、aeiaei から、beibei を通らずに移動できる頂点全てに xixi 加算する
    • ti=2ti=2 なら、辺 eiei について、beibei から、aeiaei を通らずに移動できる頂点全てに xixi 加算する
  • 最終的な各頂点の値を求めよ
  • 1N,Q2×1051N,Q2×105

解法

木なので、辺の一方の頂点から、もう一方を通らずに移動できる頂点は、部分木となる。

○--○--○--,      ,--○
        ○--A----B--○

クエリ毎に部分木全てに xixi を足していると時間がかかるので、AやBに「この下には全部 xixi が足されますよー」という情報を記録しておいて最後に復元すれば、全体を辿るのは1発で済む。

    ○
   /  \
  A  ○    B以下の部分木に足す場合、Bにのみ「xi」足されたことを記録しておいて、
  /\   \    最後に根から集計する
 B○  ○
 /\
●●

根は適当な1頂点を決めればよいが、その場合、AとBの位置関係によっては足したい範囲を1つの部分木で表現できない場合もある。

    ●
   /  \
  A  ●    A以下の部分木に足したい……
  /\   \    Aに記録しても根から辿ったときに反映できない……
 B●  ●
 /\
○○

その場合、いったん、全体に xixi 足してしまう。
その上でB以下の部分木にそれを打ち消すように「xixi」足すと考えればよい。

これで「部分木以下全ての頂点に加算」という1つの操作だけにクエリを分解でき、根からの集計が可能になった。

各クエリで、上記どちらの方法を採るかを決めるのに、辺のどちらの頂点が根に近いかを判別する必要がある。
まず最初に根から探索して各頂点の深さなり、親なりを計算しておくと、判別できる。

Python3

F - Close Group

問題

  • NN 頂点 MM 辺の単純無向グラフが与えられる
  • ここから辺を取り除き、「どの連結成分も、それ単体では完全グラフ」となるようにしたい
  • あり得る最小の連結成分の個数を求めよ
  • 1N181N18

解法

制約がbit演算を物語る。

頂点集合をbitで管理する。全ての集合の組合せの数は多くとも 218=262144218=262144 で、線形な計算が可能。

             76543210
{1,2,4,5} → 00110110 = 54

その上で、以下を事前計算しておく。

  • cmp[S]=Scmp[S]=S で表される頂点集合は、1つの連結成分にできる(誘導部分グラフが完全グラフとなる)

次に以下のDPをする。

  • dp[S]=Sdp[S]=S で表される頂点集合からなる誘導部分グラフが入力だった場合の答え

'1,10,100,…' については自明に1となる。

遷移は、以下のように考えられる。

S = 1101101

これを2グループに分ける分け方を全探索する

1001001  このように分けたとする
0100100

1001001  を1つの連結成分に出来るか? を cmp より取得
  できないなら次へ
  できるなら、そう分けたときの答えは DP[0100100] + 1

全分け方について調べ、その中での最小値が、DP[S] となる

最終的に DP[111...1]DP[111...1] が答え。

このように、「NN 個のbitの集合 2N2N 個をイテレートしながら、更にその内部で部分集合をイテレートする」操作は、計算量 O(3N)O(3N) となる。
「最初のbit集合に選ばれない」「最初のbit集合には選ばれたが、部分集合には選ばれない」「部分集合にも選ばれる」の3通りを、各bitが独立に持つと考えればよい。

あるbitの部分集合をイテレートする方法は、以下参照

Python3

解法2

この問題設定自体、最小クリーク被覆問題 と名前が付いているらしい。

それによると、補グラフ(元のグラフから各頂点間の辺の有無を反転させたグラフ)を考えると、彩色問題に帰結できる。

彩色問題は、グラフ上で直接辺で繋がった2頂点は別の色で塗る、というルールで、kk 色で矛盾無く塗れますか or 何色あれば塗れますか、という問題。

「補グラフで辺がある2頂点は別の色」なので、「同じ色の2頂点なら元のグラフで辺がある」ことがいえる。

彩色可能な最小色数を求めればよい。

詳細なアルゴリズムは未調査だが、これは O(2NN)O(2NN) でできるらしい。

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