Sky株式会社プログラミングコンテスト2026(AtCoder Beginner Contest 455)F,G問題メモ

F - Merge Slimes 2

問題文

  • 長さ $N$ の非負整数列 $A$ があり、最初はすべての要素が $0$ です。
  • $Q$ 個のクエリが与えられるので順に処理してください。
  • $q$ 番目のクエリでは $1 \leq l_q \leq r_q \leq N$ を満たす整数 $l_q,r_q$ と正整数 $a_q$ が与えられるので、以下を順に行ってください。
    • $A_{l_q}, A_{{l_q}+1}, \dots, A_{r_q}$ に $a_q$ を足す。
    • その後、$M=r_q-l_q+1, \; B=(B_1,B_2,\dots,B_M)=(A_{l_q}, A_{l_q+1}, \dots, A_{r_q})$ として、以下の問題の答えを求める。
      • $M$ 個のスライム $1,2,\dots,M$ があって、$m$ 個目のスライムの重さは $B_m$ です。
      • $2$ 個のスライムを選び、合成させるという操作を $M-1$ 回繰り返します。
      • 重さが $x,y$ のスライムを合成させると重さ $x+y$ のスライムが出現し、元の $2$ 個のスライムは消えます。このとき $x \times y$ のコストがかかります。
      • $M-1$ 回の操作のコストの総和としてあり得る値の最小値を $998244353$ で割った余りを求めてください。
  • 各クエリでの $A$ に対する変更内容はその後にも引き継がれることに注意してください。

制約

  • $1 \leq N \leq 10^5$
  • $1 \leq Q \leq 10^5$
  • $1 \leq l_q \leq r_q \leq N$
  • $1 \leq a_q \leq 10^9$
  • 入力はすべて整数

解法

クエリで、スライムの重さを増加させるのは区間に対してなのに、 合成するのはどのスライムからでもよい、というのが一見難しそう。

ひとまず、$B=(1,2,3,4,5)$ など適当な列を、 左から順に合成した時のコストと、右から順に合成した時のコストを計算してみると、全く同じ値になることが分かる。

このことから、コストは合成順に依らず、 「隣り合うスライムから順次マージしてよさそう」と予測が付けられる。

ちゃんと確かめると

スライムの重さは区間加算なので、遅延セグ木に載せたい。載せる情報としては、複数の実装が考えられるが、

  • $L$: 区間長
  • $S$: 区間内の $B_i$ の総和
  • $C$: 区間内のスライムを1つにするまでの合成コスト

の3つを載せることを考えてみる。

マージ、作用素の合成(composition)は簡単。
作用素 $f$ を区間に反映(mapping)させた時、コスト $C$ がどう変化するかが少し難しい。

区間内の要素の1つ i と 他全部 との追加コストを考えたとき、

    ■  ■    □: 元からある分
■  □■□    ■: 増加する分(f)
□×□□□■
□  □□□□
 i
  • □×■ の増加分を、全ての i について合計した結果
    • $\displaystyle \sum_i B_i \times f(L-1) = Sf(L-1)$
  • ■×■ の増加分
    • $f^2 \dfrac{L(L-1)}{2}$

これだけ増加することになるので、ちゃんと既存の情報から計算できる。

Python3

G - Balanced Subarrays

問題文

  • 長さ $N$ の正整数列 $A$ が与えられます。
  • $A$ の空でない連続部分列は $\frac{N(N+1)}{2}$ 個ありますが、そのうち以下の条件を満たす数列 $X$ をバランスの良い数列と呼ぶことにします。
    • $X$に含まれる各整数は $X$ 内に同じ回数登場する
  • $k=1,2,\dots,K$ について、以下の $2$ 種類の値をそれぞれ求めてください。
    • バランスの良い数列のうち、各整数が $B_k$ 回ずつ登場するものの個数
    • バランスの良い数列のうち、$B_k$ 種類の整数が登場するものの個数
  • ただし、$2$ つの連続部分列は、$A$ から取り出す場所が異なれば数列として同じでも区別して数えることに注意してください。
  • $1$ つの入力につき、$T$ 個のテストケースを解いてください。

制約

  • $1 \leq T \leq 2 \times 10^5$
  • $1 \leq N \leq 2 \times 10^5$
  • $1 \leq K \leq \min(N,10)$
  • $1 \leq A_i \leq N$
  • $1 \leq B_k \leq N$
  • $B_1,B_2,\dots,B_K$ は互いに相異なる
  • 全てのテストケースにおける $N$ の総和は $2 \times 10^5$ 以下
  • 入力される値は全て整数

解法

いずれも、うまいハッシュを作り、その累積和 $S_0,S_1,...,S_N$ に対し $S_r=S_{l-1}$ を満たすなら、十分な高確率で区間 $[l,r]$ が数え上げ対象である、という解法を用いる。

$B$ 回ずつ登場

こっちはまだ簡単。Zobrist Hash などが使える。

(値 × 登場回数 mod B) ごとに、ランダムな値を割り当てる。ただし、登場回数は 0-indexed とする。

$B-1$ 回目の登場時は、$0~B-2$ 回目の値の総XORの値を割り当てる。

B=5             値   1  ...
登場回数 mod B 0  0100  ┐
               1  1010  │
               2  1011  ├ランダムな値(実際はもっと桁数を増やしたもの)
               3  0011  ┘
               4  0110  ←0~3のXORで自動的に決まる

こうすると、たとえば値 $1$ が $B$ 回登場するごとに、$1$ に由来するXORは $0$ になる。 全ての値が揃って $B$ 回登場する数列は、全てのXORが揃って $0$ になる。

割り当てたランダム値の、先頭からの累積XORを $S_1,S_2,...,S_N$、便宜的に $S_0=0$ とすると、 $S_r \oplus S_{l-1} = 0$ であるような区間 $[l,r]$ は、 高確率で「各要素が $B$ の倍数回登場する数列」であると言える。(ちょうど $B$ 回とは限らない点に注意)

尺取法などで、左端 $l$ ごとに「最も多く登場する要素の個数が $B$ 回以下である最長の $r$」を求め、 $S_l,S_{l+1},...,S_{r}$ の中で $S_{l-1}$ と等しいものの個数を求めていけば、$2B,3B,...$ 回である可能性を除外できる。

$B=1$ の時は、少し例外処理が必要となる。ランダム値は必要なく、 単に左端 $l$ ごとに「最も多く登場する要素の個数が $1$ 回以下である最長の $r$」に対し、$r-l+1$ 個の区間が条件を満たす。

$B$ 種類が登場

こっちのテクニックが、あまり見たことなかった。

XORでなく、和でハッシュを作る。
つまり、(回数は関係なく)$A$ の各値にランダムな値を割り当て、先頭からの累積和を $S_1,...,S_N$ とする。

         A     3  1  4  1  2  2  3
ランダム値     5  2  3  2  6  6  5  (実際はもっと桁の大きな値を使用)
         S  0  5  7 10 12 18 24 29

数え上げ対象数列における「$B$ 種類の値の集合」を固定する。
その集合内の要素に割り当てられたランダム値を1回ずつ合計した値を $H$ とする。

すると、良い数列 $[l,r]$ において、$S_r-S_{l-1}=H \times \dfrac{r-l+1}{B}$ が成り立つ。これを整理すると、

  • $S_r \times B - H \times r = S_{l-1} \times B - H \times (l-1)$

が成り立つ。$T_i = S_i \times B - H \times i$ として、$T_{l-1}=T_r$ となる区間数を求める的なことをしたい。

だが、$H$($B$ 種類の値の集合)は様々に変わる。どの $H$ で $T_i$ を計算すれば良いかの候補も定まらないし、様々な $H$ を乱用しすぎるとハッシュ衝突も気になる。

ここで、尺取法で、区間の左端 $l$ ごとに以下を計算する。

  • $r_{lo}:=l$ からちょうど $B$ 種類目の要素が初めて登場する index
  • $r_{hi}:=l$ からちょうど $B+1$ 種類目の要素が初めて登場する index
    • いずれも、$l$ 以降に $B,B+1$ 種類現れない場合は $N$

各 $l$ に対して、そこを左端とするバランスの良い数列における「$B$ 種類の値の集合」、ひいては $H$ は一意に決まる。

また、$l$ に対して「ちょうど $B$ 種類の値が出現する」ような $r$ は $[r_{lo}, r_{hi})$ の範囲となる。
この範囲の $T_r$ のうち、$T_{l-1}$ と等しい値の個数が、$l$ を左端とした良い数列の個数となる。

$l$ を固定すると、良い数列になるための「$H$ が決まる」「$r$ の範囲も限定される」ことで、計算対象が明確になり、ハッシュ衝突の懸念もぐっと減る。

しかし、今度は「様々な $l$ に対して、その $l$ に対応する $H$ で $[r_{lo},r_{hi})$ の範囲の $T_r$ を計算していったら、TLEになるのでは?」という懸念がある。

だがよく考えると、$l$ から $l+1$ に移るとき、、、

  • $l$ と $l+1$ で $H$ が変わらないなら、計算した各 $T_r$ はそのまま使える。
    • ※ $r_{lo}$ は変わる可能性があるため、個数を増減させる必要はある。まぁサボっても衝突の可能性は低い?
  • $l$ と $l+1$ で $H$ が変わる場合、$l$ における $r_{hi}$ が、$l+1$ における $r_{lo}$ になる。
    • $l$ の時に $T_r$ を求める $r$ の範囲と、$l+1$ の時の $r$ の範囲は、被らないことが保証される。

何度も同じ $r$ に対し、様々な $H$ で $T_r$ を再計算する必要は無く、全体 $O(N)$(連想配列にlogが付く場合は $O(N \log{N})$)で求められることが分かる。

Python3

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