estie プログラミングコンテスト2025 (AtCoder Regular Contest 210)A,B,C問題メモ

estie プログラミングコンテスト2025 (AtCoder Regular Contest 210)

う~む、昨日のABCに続きあっさり解けるはずの問題でハマってるなあ。

A - Always Increasing

問題文

  • 長さ $N$ の正整数列 $A=(A_1,A_2,\ldots,A_N)$ に対して,$Q$ 回の操作を行います.
  • $q$ 回目の操作は $A_{i_q}$ に $x_q$ を加算するというものです.
  • あなたの目標は,$A$ の初期状態を適切に定めることで,どの時点においても(つまり,初期状態および各操作の直後においても)以下の条件が成り立つようにすることです.
    • $0\lt A_1 \lt A_2 \lt \cdots \lt A_N$ が成り立つ.
  • この目標を達成するとき,$A$ の初期状態における要素の総和として考えられる最小値を求めてください.

制約

  • $2\leq N\leq 2\times 10^5$
  • $1\leq Q\leq 2\times 10^5$
  • $1\leq i_q\leq N$
  • $1\leq x_q\leq 10^5$
  • 入力される値はすべて整数.

解法

「初期状態」を求めるというのが罠(?)で、「最終状態」を考えて、そこから途中で足された $x_q$ を全て引く方が考えやすい。

また狭義単調増加は考えづらいので、$0$ 以上の広義単調増加として答えを求め、後から全てに $A_i+=i$ するとよい。

差分配列 $D_i=A_{i}-A_{i-1}$ を考える。先頭と末尾にもとりあえず値は持っておく。

i     1   2   3   4
A     0   2   5   5
i   1   2   3   4  (5)
D   0   2   3   0   0

$A_i$ に $x$ を足すということは、$D_i$ に $x$ を足し、$D_{i+1}$ から $x$ を引くことに相当する。

この時、$D_{i+1} \lt 0$ になってしまうと単調増加に違反してしまう。
負の場合はここを強制的に $0$ にすることで単調増加を保てる。
(言い換えると、「元から、ギリギリ負にならない値だったことにしてしまう」)

$D=(0,0,...)$ からスタートして、負が発生したら $0$ にするという方針で最後まで $D$ を更新して累積和で復元すると、 最終状態として最小の $A$ が求まる。
その総和から、途中で足された $x_q$ を全て引くと答えとなる。

Python3

B - Remove Median Operations

問題文

  • 長さ $N$ の整数列 $A=(A_1,A_2,\ldots,A_N)$ および,長さ $M$ の整数列 $B=(B_1,B_2,\ldots,B_M)$ が与えられます.
  • ここで $N$ は偶数です.
  • $Q$ 個のクエリが与えられます.
    • $t_q=1$ の場合:$A_{i_q}$ を $x_q$ に変更する.
    • $t_q=2$ の場合:$B_{i_q}$ を $x_q$ に変更する.
  • クエリを処理するたびに,次の小問題の答を求めてください.
    • 多重集合 $X$ を $X=\lbrace A_1,A_2,\ldots,A_N\rbrace$ で初期化します.
    • $j=1,2,\ldots,M$ に対して次の操作を行います:
      • $X$ に $B_j$ を挿入する.その後,$X$ の中央値を $X$ からひとつ削除する.
    • すべての操作が終了したときの $X$ の要素の総和を求めてください.

制約

  • $1\leq N,M,Q\leq 2\times 10^5$
  • $N$ は偶数.
  • $1\leq A_i\leq 10^9$
  • $1\leq B_i\leq 10^9$
  • $t_q\in\lbrace 1,2\rbrace$
  • $t_q=1$ ならば $1\leq i_q\leq N$
  • $t_q=2$ ならば $1\leq i_q\leq M$
  • $1\leq x_q\leq 10^9$
  • 入力される値はすべて整数.

解法

仮にクエリが無く、初期状態の $A,B$ で小問題を素直に解くことを考えると、以下のイメージになる。

  • $A$ を値の小さい方と大きい方 $\frac{N}{2}$ 個ずつに分ける。
    • 小さい方は、大きい値から取り出せるヒープキュー $S$ に入れる。
    • 大きい方は、小さい値から取り出せるヒープキュー $L$ に入れる。
  • $j=1,2,...,M$ の順に、$B_j$ をどっちかに入れるか、捨てる。
    • $B_j \lt S.top$ なら、$S$ に入れて、最大値を取り出して捨てる。
    • $S.top \le B_j \le L.top$ なら、$B_j$ を捨てる。
    • $L.top \lt B_j$ なら、$L$ に入れて、最小値を取り出して捨てる。
  • 最終的な $S,L$ に入っている値が、$X$ に残っている要素である。

この操作を考えると、結局、 「$A$ と $B$ を合わせた多重集合から、小さい方 $\frac{N}{2}$ 個と大きい方 $\frac{N}{2}$ 個」が残ることになり、 $A$ や $B$ の順番には依らないことが分かる。

略証

座標圧縮をした上で、FenwickTreeを2本用意して値毎の個数と総和を管理しておく。

変更クエリは特に難しくない。

値を求める際は、個数のFenwickTree上で二分探索して、上下から $\frac{N}{2}$ 個となる箇所を見つければよい。

同じ値が複数含まれる場合に注意。
「$\frac{N}{2}$ 個目に当たる値は $3$ だけど、$4$ 個ある $3$ の中で $2$ 個しか要らない」みたいなことがあるので、 余剰分は加えないようにする。

Python3

C - Fair Coin Partition

問題文

  • $N$ 種類のコインがあります.
  • $i=0,1,...,N-1$ 種類目のコインは価値が $10^{i}$ で,あなたはこのコインを $A_i$ 枚所持しています.
  • これらのコインを,$M$ 人に配ることにしました.
    • ただし,$1$ 人あたりに配るコインの価値の総和が等しくなるようにします.
    • また,配らないコインがあってもよいです.
  • $1$ 人あたりに配るコインの価値の総和としてありうる最大値を求めてください.
  • $T$ 個のテストケースが与えられるので,それぞれについて答えを求めてください.

制約

  • $1\leq T\leq 10^5$
  • $1\leq N\leq 2\times 10^5$
  • $1\leq M\leq 10^9$
  • $0\leq A_i\leq 10^9$
  • すべてのテストケースにわたる $N$ の総和は $2\times 10^5$ 以下.
  • 入力される値はすべて整数.

解法

$10^{200000}$ など桁数がとんでもないことになるので、 問題を解きつつ巨大な整数を上手く管理する、という複合的な問題となる。

まず、以下の情報を計算する。

  • $C_i:=$ コイン $i-1$ 以下のコインの総額は、コイン $i$ 何枚分に相当するか(切り捨て)

$C_0=0$ であり、$i=1,2,...$ の順に $C_i=(A_{i-1}+C_{i-1})/10$ として計算できる。

例えば 以下のようになる。$N=5$ で、$M=7$ 人に配るとする。

N=5  M=7
i   0   1   2   3   4
A  10 650 500  50  20
C   0   1  65  56  10

まずは最大価値のコイン4の分配を考える。

コイン4だけなら $20/7=2$ 枚ずつしか配れないが、 $C_4$ も含めて考えると $(20+10)/7=4$ 枚(に相当する価値)ずつ配ることができる。
この時、コイン3以下からは、コイン4の $配る枚数 \times M - A_4 = 4 \times 7 - 20 = 8$ 枚分の価値を借りていることになる。

「コイン3以下から、コイン4の $8$ 枚分の価値を借りている」という情報を持ちつつ、 コイン4を $4$ 枚ずつ配ったことにする。

コイン $i$ の価値はコイン $i-1$ の価値の倍数なので、総額さえ足りていれば必ず分配できる。
言い換えると、コイン3以下から構成される「総額 $k \times 10^4$ のコイン群」は、必ず「総額 $10^4$ のコイン群 $k$ 個」に分割でき、1人1人にコイン4の $1$ 枚相当のコイン群を配ることができる。(「総額では足りているが、きっちりと $10^4$ のコイン群 $k$ 個に分ける手段がない」可能性は考慮しなくていい)

※この方法とは逆に、「コイン4は $2$ 枚ずつ配り、残りをコイン3 $60$ 枚分の価値として繰り下げる」という方法をとると、 「コイン4を0.5枚Aさんに、0.5枚Bさんに分配する」みたいな分配方法まで可能だと見なしてしまう場合があるため上手くいかなくなる。

次、コイン3を何枚ずつ分配するかだが、借金をまず返す。
コイン3の $1$ 枚とコイン2の $10$ 枚では、コイン2の方が分配の柔軟性の面で必ず優位なので、 借金はなるべく大きいコインで返してよい。

現在の借金はコイン4 $8$ 枚分=コイン3 $80$ 枚分に当たるが、 コイン3は $50$ 枚しかないので、借金は返しきれない。 この場合、コイン3 $30$ 枚分の借金が残っているとして、次に引き継ぐ。コイン3の分配は $0$ 枚となる。

コイン2では借金を返しても $200$ 枚残る。残りをコイン4の時と同様に分配する。
$(A_2の残り + C_2)/M = (200+65)/7 = 37$ 枚ずつ配れる。借金はコイン2 $37 \times 7 - 200 = 59$ 枚分となる。

これをコイン0まで繰り返すと、各コイン(に相当する価値)を何枚ずつ配れるか、求められる。

       i   0   1   2   3   4
配る枚数   1   8  37   0   4

後は、下の桁から繰り上げていき、各枚数を $10$ 未満にする。
逆転させて文字列として結合すれば、答えとなる。

       i   0   1   2   3   4
配る枚数   1   8   7   3   4    →  43781

Python3

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