AtCoder Beginner Contest 440 (Promotion of Engineer Guild Fes) E,F問題メモ

E - Cookies

問題文

  • $N$ 種類のクッキーがそれぞれ $10^{100}$ 枚あります。
  • $i$ 種類目のクッキーの $1$ 枚あたりの美味しさは $A_i$ です。
  • これらのクッキーから合計で $K$ 枚を選びます。
    • クッキーの選び方は、選んだクッキーの種類の多重集合が一致するときかつその時に限り同じとみなします。
  • 全ての選び方 $\binom{N+K-1}{K}$ 通りそれぞれについて、選んだクッキーの美味しさの和を考えます。これらを大きな値から順に重複を込めて $S_1,S_2,\dots$ とするとき、$S_1,\dots,S_X$ を求めてください。

制約

  • $1\leq N \leq 50$
  • $1 \leq K \leq 10^5$
  • $1 \leq X \leq \min\left(10^5, \binom{N+K-1}{K}\right)$
  • $-10^9 \leq A_i \leq 10^9$
  • 入力は全て整数

解法

Dijkstra風に、大きい方から探索する。

$A_1 \ge A_2 \ge ... $ となるように index を振り直す。
「$c_i:=$ クッキー $i$ を選んだ個数」とし、クッキーの選び方を $C=(c_1,c_2,...,c_N)$ のように $c_i$ の列で表す。 選び方に対するスコア(美味しさの和)は、$\sum_i A_i c_i$ である。

一番大きい $S_1$ の選び方は $(K,0,0,...,0)$ であり、この時 $A_1 \times K$ である。
次に大きい $S_2$ の選び方は $(K-1,1,0,...,0)$ である。

この次の $S_3$ は、$(K-2,2,0,...,0)$ と $(K-1,0,1,0,...,0)$ の2つの選択肢があるが、 しかし、この2つのいずれかに限られる。

これを繰り返し、次々と

  • 暫定でスコアが最大になる選び方を、次の $S_i$ として確定させる
  • その選び方の次にスコアが大きい可能性のあるものを優先度キューに入れる

として探索を広げていけば、Dijkstra と似た要領で大きい順に確定していける。

ただし、遷移が重複してはいけない。
言い換えると、選び方 $C_x$ が、選び方 $C_y$ からも $C_z$ からも 「次に大きい可能性のあるもの」として探索されてしまってはいけない。
$C_x$ のスコアが2回計上されてしまい、誤った答えになる。

単純なDijkstraであれば、探索済みの頂点番号を記録して探索済みなら遷移しないようにすればよいのだが、 今回のような、複数の値からなるものを頂点として扱う場合、 探索済みかどうかを調べるのにも時間がかかるため、始めから「重複しないような遷移方法」を決めるのが大切となる。
(今回は $N$ が小さいため、これでも間に合うようだが)

次のような法則で探索を広げるとよい。

  • $c_i$ が $0$ でない最も右のindexを $i$ とする
  • 以下の2通りに遷移できる。
    • $c_{i-1}$ を1減らし、$c_i$ を1増やす($c_{i-1} \gt 0$ の場合)
    • $c_i$ を1減らし、$c_{i+1}$ を1増やす($i \lt N$ の場合)

この遷移のルールで探索が重複しない略証

よって、$(score, i, c_{i-1}, c_i)$ を優先度付きキューに入れ、大きい順に取り出せば、 適切にスコアを差分更新し、次に大きい可能性のある選び方に遷移できる。

$N=1$ の時、実装によっては配列外参照を起こすので注意。

Python3

F - Egoism

問題文

  • $N$ 頭の馬がおり、$1$ から $N$ までの番号が付けられています。
  • 馬 $i$ ($1 \leq i \leq N$) は機嫌 $A_i$ と丁寧さ $B_i$ をもちます(ここで、$B_i$ は $1, 2$ のいずれか)。
  • 夜になると、$N$ 頭の馬が順番に $1$ 回ずつ水浴びをします。$j$ 番目 ($1 \leq j \leq N$) に水浴びをする馬の番号を $p_j$ とおくと、馬 $p_j$ の満足度が以下で与えられます。
    • $j = 1$ の場合、馬 $p_j$ の機嫌。
    • $j \geq 2$ の場合、馬 $p_j$ の機嫌と馬 $p_{j-1}$ の丁寧さの積。
  • これから $Q$ 日にわたって毎日 $1$ つずつクエリが与えられるので、順に処理してください。$k$ 日目 ($1 \leq k \leq Q$) のクエリは以下の通りです。
    • 馬 $W_k$ の機嫌を $X_k$ に、丁寧さを $Y_k$ に変更する(ここで、$Y_k$ は $1, 2$ のいずれか)。
    • その後、その夜に $N$ 頭の馬が水浴びをする順番を任意に決められるとき、その夜の $N$ 頭の水浴びの満足度の総和が最大でいくらになるかを求める。

制約

  • $1 \leq N \leq 2 \times 10^5$
  • $1 \leq Q \leq 2 \times 10^5$
  • $1 \leq A_i \leq 10^6$ ($1 \leq i \leq N$)
  • $B_i$ は $1,2$ のいずれか ($1 \leq i \leq N$)
  • $1 \leq W_k \leq N$ ($1 \leq k \leq Q$)
  • $1 \leq X_k \leq 10^6$ ($1 \leq k \leq Q$)
  • $Y_k$ は $1,2$ のいずれか ($1 \leq k \leq Q$)
  • 入力される値はすべて整数

解法

丁寧さが $2$ である馬を「丁寧な馬」、$1$ である馬を「丁寧でない馬」とする。

基本的に、機嫌がいい馬を丁寧な馬の次に持ってくるのがスコア(満足度の総和)が高くなる。
なんとなく、機嫌と丁寧さが決まっているとき、スコアの最大値は以下のように求められそうである。

  • なんとなく解法(WA)
    • 丁寧な馬の頭数を $k$ とする
    • その時点で機嫌のいい馬の上位 $k$ 頭の機嫌 $A_i$ の和を $S$ とする。
    • それ以外の機嫌 $A_i$ の和を $T$ とする。
    • 機嫌のいい $k$ 頭を丁寧な馬の次に持ってくることで、$S$ を2倍でスコアに寄与させられる。
      • $2S+T$ がスコアの最大値となる。

大まかには正しいのだが、しかし、以下の点で誤りがある。

  • 上位 $k$ 頭に、丁寧な馬が全て入っている場合
    • 丁寧な馬は、少なくとも1頭は、丁寧でない馬の後(または先頭)に置く必要がある。
    • この場合は、丁寧な馬の中で $A_i$ 最小の馬を犠牲にし、代わりに $k+1$ 番目に $A_i$ が大きい馬を $S$ に採用するのが最適
  • $N$ 頭全てが丁寧な馬の場合
    • どれかは先頭に置く必要があるので、2倍にできるのは $N-1$ 頭までとなる。

この2つを解消するために、各時点で以下が取得できるようなデータ構造を管理したい。

  • $X_k:=$ 全ての馬の機嫌 $A_i$ のうち、上位 $k$ 個の和(任意の $k$ につき)
  • $Y:=$ 丁寧な馬のみの中で、機嫌 $A_i$ の最小値
  • $Z:=$ 丁寧な馬の頭数

すると、適切な解法では、以下のようにスコアを求められる。

  • 正しい解法
    • 丁寧な馬の頭数を $k$ とする
      • ただし、$k=N$ の時、代わりに $k=N-1$ とする。
    • $X$ から $Y$ を一時的に取り除く。その後、$X_k$ を取得し、$S$ とする。
    • それ以外の機嫌の和を $T$ とする。($Y$ を含め)
    • $2S+T$ がスコアの最大値となる。

$X$ をSortedSet、$Y,Z$ を削除可能な優先度付きキューなどで管理することで、クエリ毎に更新・取得できる。

(ただし、Pythonの場合TLEが取れなかったため、$X$ は個数と値をそれぞれ管理するFenwickTree2本で実装して通った)

Python3

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