Processing math: 100%

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

E - Vitamin Balance

問題文

  • N 個の食べ物があり、それぞれの食べ物にはビタミン 1,2,3 のうちちょうど 1 つのみが含まれています。
  • 具体的には、i 個目の食べ物を食べると、ビタミン ViAi だけ摂取でき、またカロリーが Ci だけ摂取されます。
  • 高橋君は、摂取するカロリーが合計で X 以下となるように、N 個の食べ物のうちいくつか(0 個でも良い)を選んで食べることができます。
  • このとき、「ビタミン 1,2,3 のうちもっとも摂取量が少ないものの摂取量」としてあり得る最大の値を求めてください。

制約

  • 1N5000
  • 1X5000
  • 1Vi3
  • 1Ai2×105
  • 1CiX
  • 入力はすべて整数

解法

各食べ物に入っているビタミンはちょうど1種類だけなので、 ビタミンの種類でそれぞれ分けてナップサックDPをすれば、 「カロリー x 以下で摂取できるビタミン v の最大量」がそれぞれ求まる。
(「ちょうど x」でなく、「x 以下」の最大値を求めておく)

このDP配列があれば、ある k に対し「ビタミン3種をどれも k 以上摂取するのに必要なカロリー」は、 3つのDP配列を二分探索することでおこなえる。

k 自体も二分探索することで、O(NX+logXlog(NAmax)) で答えを求められる。

Python3

F - Double Sum 3

問題文

  • 長さ N の整数列 A=(A1,A2,,AN) が与えられます。
  • 1LRN を満たす整数の組 (L,R) に対し f(L,R) を以下のように定義します。
    • 何も書かれていない黒板に RL+1 個の整数 AL,AL+1,,AR を順に書き込む。
    • 以下の操作を黒板に書かれた整数が全て消えるまで繰り返す。
      • 整数 l,r を選ぶ。ただし、 lr かつ黒板に l 以上 r 以下の整数が全て 1 つ以上書かれているように l,r を選ぶ必要がある。その後、黒板に書かれた l 以上 r 以下の整数を全て消す。
    • 黒板に書かれた整数が全て消えるまでに必要な操作回数の最小値を f(L,R) とする。
  • NL=1NR=Lf(L,R) を求めてください。

制約

  • 1N3×105
  • 1AiN
  • 入力される値は全て整数

解法

区間が与えられたら、最適な操作は一意に決まる。
たとえば、残っている中で最小の要素を l とし、そこから1ずつ増やしていって黒板になくなる直前の要素を r とすればよい。

ある操作によって指定する (l,r) の、r を「リーダー」とする。
区間内に r が複数ある場合は、一番最初に出てくるものをリーダーとすることにする。

各要素につき「自身がリーダーとなるような区間 (L,R)」の個数を数えて合計すると、答えと同じことになる。

そのような区間は、以下のように考えられる。

5 1 9 3 5 2 6 9 1 6
        *            ←この5にとって、自身がリーダーとなる区間は、
 |<----|               左端は、「自身または自身+1の要素が出てくるまで」
         |->           右端は、「自身+1の要素が出てくるまで」

これより区間を広げたら、「リーダーを委譲する他の要素が必ず入ってくる」ことになる。
上例の場合は、左端と右端から独立に選んで、4×2=8 通りの区間で5がリーダーとなる。

各要素につき、「A 上で自身から前後方向それぞれに、Ai または Ai+1 が出てくる直近のindex」を取得できればよい。

値毎に出現indexを列挙しておいて、二分探索すると可能となる。

Python3

G - Permutation Concatenation

問題文

  • 正整数 N が与えられます。
  • 長さ N の正整数列 A=(A1,A2,,AN) に対し f(A) を次の手順で得られる整数とします。
    • S を空文字列とする。
    • i=1,2,,N の順番で以下の操作を行う。
      • Ai を先頭に余分な 0 を付けない十進数の文字列としてみたものを T とする。
      • S の末尾に T を連結する。
    • 最終的な S を十進数の整数としてみた値を f(A) とする。
    • 例えば A=(1,20,34) に対し f(A)=12034 です。
  • (1,2,,N) の順列 P は全部で N! 通りありますが、それら全てに対する f(P) の総和を 998244353 で割ったあまりを求めてください。

制約

  • 1N2×105
  • 入力される値は全て整数

解法

数式こねくり回して畳み込みの形に持っていく系問題。

主客転倒して、「ある値 x が答えに寄与する量」を考える。

ある順列に固定した時の x の寄与は、

N = 15    x = 4

o o ... o o 4 o o ... o o
              ~~~~~~~~~~~
       ここに置く数の桁数の合計を s とし、4 * 10^s だけ寄与する

x の寄与を求める時に、x 以外の数は「桁数」だけが重要で、具体的な値は重要ではない。
よって、桁数ごとに同一視して考える。 ただ、場合の数を求める上で「桁数毎の個数」は必要なので、それは考慮する。

制約から、桁数は最大でも6までしかない。
1桁の数が9個、2桁が90個、3桁が900個、、、ある。
N と同じ桁数の数の個数はこの限りではないが、6 桁とすると N99999=N(10611) のように求められる。
また、x と同じ桁数の数は、x 自身があるので1少ない。

上記を踏まえた d 桁の数の個数を Cd とする。

x 以外の N1 個の要素のうち、 1桁のものが i 個、2桁のものが j 個、3桁のものが k 個、、、 x の右に置かれるような順列を考える。(式の見やすさのため、とりあえず3桁までを考えるとする)

  • 桁毎の具体的な要素の選び方が (C1i)(C2j)(C3k) ある。
  • 右側の要素の並べ方が (i+j+k)!、左側の並べ方が (N1ijk)! ある。
  • 以上のような順列のそれぞれに付き、1つあたり x×10i+2j+3k だけ寄与する。

つまり、以下が、答えに対する x の寄与となる。

  • C1i=0C2j=0C3k=0x×10i102j103kC1!i!(C1i)!C2!j!(C2j)!C3k!(C3k)!(i+j+k)!(N1ijk)!

これは「i だけに依存する項」「j だけに依存する項」「k だけに依存する項」「i+j+k に依存する項」「定数項」の積に分解できる。形式的冪級数を使って以下のように表して、

  • I(x)=C1t=0(10t1t!(C1t)!)xt
  • J(x)=C2t=0(102t1t!(C2t)!)xt
  • K(x)=C3t=0(103t1t!(C3t)!)xt
  • S(x)=N1t=0t!(N1t)!xt

畳み込みによって I×J×K を計算した後、x の次数ごとに S(x) の係数をかけることで、 i+j+k=0,1,2,...,N1 それぞれについての上記の式の答えが求められる。

実際は最大6桁の要素まで考慮する必要があるが、同じことを繰り返すだけなので同様の考え方でよい。
これで、x についての寄与が O(NlogN) で求められた。

x の桁数が同じものは寄与する係数も同じなので、まとめて求められる。
x16 桁それぞれの場合について求め、合計すれば答えとなる。

Python3

programming_algorithm/contest_history/atcoder/2025/0125_abc390.txt · 最終更新: 2025/01/27 by ikatakos
CC Attribution 4.0 International
Driven by DokuWiki Recent changes RSS feed Valid CSS Valid XHTML 1.0