Processing math: 48%

AtCoder Beginner Contest 369 E,F,G問題メモ

E - Sightseeing Tour

問題文

  • 重み付き連結無向グラフに見立てた N 個の島と M 本の橋があります。
  • i は島 Ui と島 Vi を相互に結んでおり、どちらの方向に移動するにも Ti だけ時間がかかります。
    • 自己辺はありませんが、多重辺はあり得ます。
  • Q 個の問題が与えられるので、各問題に対する答えを求めてください。i 番目の問題は次のようなものです。
    • 相異なる Ki 本の橋、橋 Bi,1, 橋 Bi,2, , 橋 Bi,Ki が与えられます。
    • これらの橋をすべて 1 回以上わたり、島 1 から島 N まで移動するために必要な時間の最小値を求めてください。
    • 与えられた橋はどの順で、またどの向きにわたってもかまいません。

制約

  • 2N400
  • N1M2×105
  • 1Ui<ViN
  • 1Ti109
  • 1Q3000
  • 1Ki5
  • 1Bi,1<Bi,2<<Bi,KiM
  • 入力はすべて整数
  • どの 2 つの島の間もいくつかの橋をわたって移動することができる。

解法

N がそこそこ小さく、Ki が特に小さい。

Froyd-Warshallで、以下を O(N3) で事前計算できる。

  • Dists[u][v]=u から v への最短距離

各クエリに付き、以下を全探索すればよい。

  • 橋を渡る順番 Ki! 通り
  • 各橋を渡る方向 2Ki 通り

この2つが決まれば、「1→1本目に通る橋の始点→終点→2本目に通る橋の始点→終点→…→N」の距離を Dists から求められる。 その中の最小値がクエリへの答えとなる。

全クエリの計算 O(QK!2KK) (※KKi の上限)は、制約値の代入でおよそ 5.76×107 となる。
制限時間が長いので、間に合う。

もちろん、bitDPをしてもよい。その方が計算量は少なく済む。

Python3

F - Gather Coins

問題文

  • H 行、横 W 列のグリッドがあります。
  • このグリッドの上から i 行目、左から j 列目にあるマスのことを (i,j) と表記します。
  • このグリッド上には N 枚のコインが落ちており、i 個目のコインはマス (Ri,Ci) を通ることで拾うことができます。
  • あなたの目標は、マス (1,1) から始めて下か右に 1 マス移動することを繰り返し、できるだけ多くのコインを拾いながらマス (H,W) まで行くことです。
  • あなたが拾うことのできるコインの枚数の最大値、およびそれを達成するための移動経路を 1 つ求めてください。

制約

  • 2H,W2×105
  • 1Nmin
  • 1\leq R_i \leq H
  • 1\leq C_i \leq W
  • (R_i,C_i)\neq (1,1)
  • (R_i,C_i)\neq (H,W)
  • (R_i,C_i) は互いに相異なる
  • 入力は全て整数

解法

H \times W が小さければ、よくある経路数え上げDPのようにできるが、今回は大きいのでTLEとなる。

  • \mathrm{DP_{TLE}}[i][j]:=ij 列までに獲得できるコインの最大枚数

ただ、H \times W の配列を一度に持たなくても、1行毎に使い回せば最大枚数だけは求められる。

  • \mathrm{DP_{枚数}}[j]:=j 列までに獲得できるコインの最大枚数

これは、DP配列をセグメント木で実装して、(r,c) に置いてあるコインを拾う場合は

  • \displaystyle \mathrm{DP_{枚数}}[c]=\max_{1 \le i \le c}(\mathrm{DP_{枚数}}[i])+1 で更新する

とすればよい。(r,c) の小さい順に処理していけばよい。
最終的な \max(\mathrm{DP_{枚数}}) が答えとなる。

経路を復元するためには、ここに「コイン i の直前には、どのコインを取ったか」の情報があればよい。

  • \mathrm{DP}[j]:=j 列までに獲得できるコインの(最大枚数, 最後に取ったコイン番号)
  • \mathrm{Predecessor}[i]:= 最大値を実現する上でコイン i の直前に取ったコイン番号

これで、最大値を得るために通過したコインをバックトレースできる。

Python3

G - As far as possible

問題文

  • N 頂点からなる木が与えられます。
    • i 番目( 1\leq i\leq N-1 )の辺は頂点 U_i と頂点 V_i を結んでおり、長さは L_i です。
  • K=1,2,\ldots, N について次の問題を解いてください。
    • 高橋君と青木君がゲームをします。ゲームは次のように行われます。
      • まず、青木君が木上の相異なる K 個の頂点を指定します。
      • 次に、高橋君は始点と終点がともに頂点 1 であるような歩道であって、青木君が指定した頂点をすべて通るようなものを構成します。
      • 高橋君が構成した歩道の長さをスコアと定義します。高橋君はスコアをなるべく小さく、青木君はスコアをなるべく大きくしたいです。
      • 2 人が最善に行動したときのスコアを求めてください。

制約

  • 2\leq N\leq 2\times 10^5
  • 1\leq U_i \lt V_i\leq N
  • 1\leq L_i\leq 10^9
  • 入力はすべて整数
  • 与えられるグラフは木である。

解法

青木君が N 頂点全てを指定したときは、明らかにオイラーツアーの距離(=全辺の長さの2倍)である。

ここから、指定頂点を減らしていくことを考える。

頂点 1 および 1 を根とした時に葉となる頂点以外は、指定から除外しても変わらない。 (結局、葉まで行かないといけないので)

さて、いよいよ葉のいずれかを指定から除外しなければいけなくなったとする。

    ①
  /  \        ○: 葉以外
 ②     ③      ●: 葉
 /\   /|\
❶④ ⑤ ❹ ⑥
  | |    /\
  ❷ ❸   ❺❻

この時、「最後の分岐から葉(自身)までの距離」が短い方から順に除外する、ということを繰り返すとよい。

❶❷にとっては最後の分岐は②、❸❹にとっては③、❺❻にとっては⑥となる。
葉を取り除くと、ゲームのスコアからは「分岐から葉までの距離の往復分」が引かれる。
なるべく青木君は引かれるスコアが小さい方から貪欲に除外していくのが適切である。(FIXME 本当に?)

ただし、各頂点にとっての “最後の分岐” は変化しうる。
例えば❺を取り除くと、❻は⑥で分岐しなくなり、“最後の分岐” がより根に近い③になる。
分岐する頂点 v の子の中で最も選ばれるのが遅い子(=最も v までの距離が遠い葉を持つ子)について、 このように最後の分岐までがより遠くなり、引かれるスコアが増加する。

以上を踏まえて、以下の木DPをすればよい。

  • \mathrm{DP}[v] := 頂点 v の部分木内で最も v から遠い頂点までの距離。
  • \mathrm{Drop} := 葉を指定から除外することで引かれるスコア差分の多重集合。最初は空で、木DPの過程で確定したものから追加。

子を複数持つ頂点では、「最も遠い葉を持つ子の値を親に引き継ぎ、残りの子の値は確定するのでDropに追加」という処理をする。

f(v) := DP[v]+(vとvの親を結ぶ辺の距離) とする。

例えば⑥を処理する段階では、

  • 2つの子の f(❺)f(❻) を比較する。ここで、f(❻) の方が大きかったとする。
  • f(❺) をDropに登録する。
  • DP[⑥]=f(❻) とし、 を除外するコストは確定させず親に引き継ぐ。

のように遷移していく。③でも同様に、

  • f(⑤),f(❹),f(⑥) を比較する。f(⑤) が最も大きいとする。
  • f(❹)f(⑥) をDropに登録する。
  • DP[③]=f(⑤) とする。

このようにしていくとDropには、いずれかの葉を指定から除外したときに引かれるスコア(の半分)が蓄積される。

ソートして小さい方から除外していくことで、K の大きい方から答えが求められる。

Python3

programming_algorithm/contest_history/atcoder/2024/0831_abc369.txt · 最終更新: 2024/09/02 by ikatakos
CC Attribution 4.0 International
Driven by DokuWiki Recent changes RSS feed Valid CSS Valid XHTML 1.0