Loading [MathJax]/jax/output/CommonHTML/jax.js

AtCoder Beginner Contest 395 F,G問題メモ

F - Smooth Occlusion

問題文

  • 高橋くんには歯が 2N 本あり、N 本は上の歯、残りの N 本は下の歯です。
  • 左から i 本目 (1iN) の上の歯の長さは Ui で、左から i 本目 (1iN) の下の歯の長さは Di です。
  • 高橋くんの歯が次の 2 つの条件をどちらも満たしているとき、歯が「うまく噛み合っている」ということにします。
    • ある整数 H が存在し、1iN を満たすすべての整数 i について、Ui+Di=H が成り立つ。
    • 1i<N を満たすすべての整数 i について、|UiUi+1|X が成り立つ。
  • 高橋くんは次の操作を好きなだけ繰り返すことができます。
    • 1 円を支払って歯削り機を使い、長さが正であるような歯を 1 つ選んでその歯の長さを 1 減らす。
  • この操作以外の方法で歯の長さを変えることはできません。
  • 高橋くんが歯をうまく噛み合わせるために支払うことになる最小の金額を求めてください。

制約

  • 2N2×105
  • 1Ui109 (1iN)
  • 1Di109 (1iN)
  • 1X109
  • 入力はすべて整数

解法

とりあえず2番目の条件だけ考えて、「歯を削る操作だけで、隣り合う上の歯の高さの差を全て X 以下にする」ことを考える。

これは、以下の操作を順におこなえば達成できる。

  1. 先頭から Ui+1=min(Ui+1,Ui+X) とする
  2. 末尾から Ui1=min(Ui1,Ui+X) とする

かつ、これが最も削る長さが最小となる。
言い換えれば、「どの削った歯も、あと1単位でも削るのが短ければ、2番目の条件を満たせない」状態となる。

操作後の上の歯の長さ配列を、元の U と区別して U とおく。

上の歯を削った後、1番目の条件について達成できる H の上限は h=min1iNUi+Di となる。
そして h で揃えるのは必ず達成できる(※)ので、U+DHN が答えとなる。

(※)上の歯で Ui>h なものは h まで削っても2番目の条件が満たされた状態は保たれる。その他は D 側を削って調整すればよい。

計算量は O(N)

Python3

G - Minimum Steiner Tree 2

問題文

  • 正整数 N,K が与えられます。また、N×N 行列 C=(Ci,j) (1i,jN) が与えられます。ここで、Ci,i=0,Ci,j=Cj,i が保証されます。
  • 頂点に 1,2,,N と番号がつけられた N 頂点の重み付き完全グラフがあります。i<j なる頂点の組 i,j について、頂点 i と頂点 j を結ぶ辺の重みは Ci,j です。
  • Q 個のクエリが与えられます。i 番目のクエリでは K+1 以上 N 以下の相異なる整数の組 si,ti が与えられるので、以下の問題を解いてください。
    • このグラフから 0 個以上好きな個数辺と頂点を取り除いてできる連結なグラフのうち、頂点 1,2,,K,si,ti をすべて含むようなものを良いグラフとよびます。
    • 良いグラフのコストを、グラフに含まれる辺の重みの総和とします。
    • 良いグラフのコストの最小値を求めてください。

制約

  • 3N80
  • 1Kmin(N2,8)
  • 0Ci,j109 (1i,jN,ij)
  • Ci,j=Cj,i (1i,jN,ij)
  • Ci,i=0 (1iN)
  • 1Q5000
  • K+1si,tiN (1iQ)
  • siti (1iQ)
  • 入力はすべて整数

解法

最小シュタイナー木問題のアレンジ。
計算量見積もりが難しい。

最小シュタイナー木は、約半年前、ABC-G で出題された。
過去問では、クエリが (si,ti) の2つではなく、1つのみの場合が求められればよかった。

シュタイナー木で必ず含めなければならない頂点を「ターミナル」と呼ぶ。
最小シュタイナー木の重みは、以下のDPで求められる。

  • DP[S,v]:= ターミナルの部分集合 S と、ターミナルとは限らない頂点 v を全て含めた部分木の最小コスト

ターミナルの個数を k とすると、完全グラフなので計算量は O(3kN+2kN2logN) がかかる。

クエリが1頂点のみなら、このDPの過程で自然と「全てのターミナルと、他のもう1頂点 v を含めた部分木の最小コスト」が求まっているので、最終的なDP配列を参照すれば求められる。

今回は2頂点だが、制約が小さいので以下で解ける。

  • 各頂点 v=K+1,...,N につき、ターミナルを {1,2,...,K,v} とした場合の最小シュタイナー木DPを前計算する。
    • v に対するDP結果を DPv とする。
  • クエリ (s,t) に対する答えは、DPs[{1,2,...,K,s},t] となる。

計算量は O(3KN2+2KN3logN) で、 制約上限の単純な代入では約 1.7×109 となり、通るのか?と不安に思うが、計算量の後ろの項(Dijkstra部分)は毎回毎回フルで回されることはなく、すぐ終わることも多いので、意外と間に合う。

また、今回はグラフが密なので、Dijkstraの実装方法も 毎回配列から暫定最小値を探すようにすると O(3KN2+2KN3) となり、二分ヒープを使うより高速となる。

N 回DPを回す上記の解法は本番でも検討したが、計算量が無理だと思って他の方法を考えてしまった。間に合うもんだね。

Python3

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