AtCoder NoviStepsを埋めてみる(22) ポテンシャル付き Union-Findの続きです。今回は累積和です。累積和を使えば大量の区間和を高速に求めることができるようになります。

累積和とは何か? 038 – How Many Guests?

038 – How Many Guests?

問題の概要

某遊園地で N 日間にわたるイベントが開催され、i 日目 には A[i] 人が来場した。
以下の Q 個の質問に答えるプログラムを作成せよ。
(質問)L[i] 日目から R[i] 日目までの合計来場者数は?

累積和を知らないならこんなコードを書くしかないですが、これでは TLE 必至です。時間計算量は O(QN) です。N, Q の値を考えると絶対に間に合いません。

ではどうするかですが、最初に i までの区間和を計算しておきます。[0, i] の区間和を sums[i + 1] に格納しておきます。

そうすると [x, y] の区間和であれば sums[y] – sums[x – 1] で求めることができます。

B – 果物の収穫

B – 果物の収穫

問題の概要

数列 A が与えられる。
連続する K 個の要素を選んだときの区間和の最小値を求めよ。

区間和の最小値を求める問題です。[i, i + K – 1] の区間和をすべて調べて最小のものを出力すればよいです。

040 – Travel

040 – Travel

問題の概要

N 個の駅があり、駅 i と駅 i + 1 距離は A[i] である。
駅 B[0] から出発し、駅 B[M – 1] まで順番に移動する場合の総移動距離を求めよ。

累積和で各駅の 駅 0 からの距離を求めれば、駅 i と駅 i + 1 間の距離は sums[Math.Max(B[i], B[i + 1])] – sums[Math.Min(B[i], B[i + 1])] で計算できます。あとはすべて足すだけです。

A – Abundant Resources

A – Abundant Resources

問題の概要

数列 A が与えられる。
連続する区間の長さが 1, 2, …, N の区間和の最大値をそれぞれ求めよ。

すべての区間和を調べ、区間の長さで分けて dic に格納します。最後にそれぞれの最大値をとればよいです。

010 – Score Sum Queries(★2)

010 – Score Sum Queries(★2)

問題の概要

クラスは 2 つあり、学籍番号 i 番の生徒のクラスは C[i] 組である。
学籍番号 i 番の生徒の試験の点数は P[i] 点であった。
学籍番号が L[j] 番から R[j] 番までの 1 組と 2 組の生徒の合計点をそれぞれ求めよ。

配列 A, B を定義し、学籍番号 i 番で 1 組であるなら A[i] に P[i]を、2 組であるなら B[i] に P[i]を代入します。A, B それぞれの累積和から学籍番号が L[j] 番から R[j] 番までの 1 組と 2 組の生徒の合計点をそれぞれ求めることができます。

C – K-bonacci

C – K-bonacci

問題の概要

正整数 N, K が与えられる。
長さ N + 1 の数列 A の各要素の値を、以下の方法で定義する。
0 ≦ i < K のとき A[i] = 1
K ≦ i のとき A[i] = A[i – K] + A[i – K + 1] + … + A[i – 1]
A[N] を 10^9 で割ったあまりを求めよ。

累積和を使えば A[i – K] + A[i – K + 1] + … + A[i – 1] を高速で求めることができます。剰余をとることで sums[i] – sums[i – K] が負数になる場合があります。この場合は 10^9を足して非負数にする必要があります。また N < K のときは常に 1 が解となります。

C – 投票 (Voting)

C – 投票 (Voting)

問題の概要

ある議題に関して「賛成」か「反対」かを問う採決が行われた。投票者は N 人である。
各人は自分の投票前にそれまでに投票した他の人がどちらに投票したかを知ることができた。
i 番目に投票した人は 自分の直前に投票した X[i] 人のうち Y[i] 人以上が賛成に投票したときだけ賛成に投票し、そうでないときは反対に投票した。
賛成に投票した人の人数を求めよ。

動的に累積和を構築する問題です。i 番目の人が投票したら i 番目までに投票した人が投じた賛成票の数を sums[i + 1] に保存しておきます。これで自分の直前に投票した X[i] 人のうち Y[i] 人以上が賛成に投票したかどうかがわかるようになります。

C – GeT AC

C – GeT AC

問題の概要

A, C, G, T からなる長さ N の文字列 S が与えられる。
l[i] 文字目から r[i] 文字目までの (両端含む) 連続部分文字列のなかに “AC” は部分文字列として何回現れるか。

配列 A を定義し、A[i] に S[i] == ‘A’ && S[i + 1] == ‘C’ であれば 1 そうでないなら 0 を代入します。あとは累積和を取れば l[i] 文字目から r[i] 文字目までに現れる部分文字列 “AC” の個数がわかります。

C – Rotate and Sum Query

C – Rotate and Sum Query

問題の概要

長さ N の整数列 A が与えられる。
Q 個のクエリを順に処理せよ。
クエリ 1:A の先頭の要素を末尾に移動させる操作を c 回おこなう。
クエリ 2:[l, r] の区間和を出力する。

区間和は累積和をつかって計算するのですが、数列の要素が変化してしまうと対応できないので クエリ 1 が来た後 クエリ 2 が来たら求める区間を適切に変更することでこれに対応します。

ただ単純に上記の処理だけだと剰余を取ったときに r ≦ l になってしまうことがあるので困ります。そこで r ≦ l のときは r に N を加算して l < r の関係が維持されるようにしています。この場合は累積和は N までではなく 2N まで計算しておく必要があります。

C – Comfortable Distance

C – Comfortable Distance

問題の概要

文字列 S が与えられる。
S[i] = S[j] (i ≦ j, L ≦ j – i ≦ R) である整数の組 (i, j) の個数を求めよ。

各 i において S[j] = S[i] ( j は [i + L, i + R]) である j の個数を数えればよいです。i 文字目までで各文字が何回出現しているかを累積和で取得しておけば、[i + L, i + R] 間にある S[i] と同じ文字の出現回数は count_sums[ch, Math.Min(i + R + 1, N)] – count_sums[ch, Math.Min(i + L, N)] で計算できます。

D – Swap and Range Sum

D – Swap and Range Sum

問題の概要

長さ N の数列 A が与えられる。
Q 個のクエリを処理せよ。
クエリ 1:A[x] と A[x + 1] の値を入れ替える。
クエリ 2:区間和 [l, r] を求める。

累積和の弱点として「値が更新されるケースに対応できない」があります。クエリ 1 で値が更新されるため、累積和では解けなさそうなのですが、隣と値を入れ替えるだけなので事前に計算しておいた累積和のうち更新が必要なのは一箇所だけです。なのでこの場合は累積和で対応できます。

C – Striped Horse

C – Striped Horse

問題の概要

1 から N の番号がついた N 個の白いマスが一列に並んでいる。
正整数 x を自由に選んで、1 ≦ i ≦ N を満たす整数 i のうち、 i + x を 2W で割った余りが W より小さくなるものはすべて黒く塗る。そのさい コスト C[i] が必要である。
コストの合計の最小値を求めよ。

「W マス連続して黒く塗り、続けて W マス連続して白いままにする」という塗り方をすべて試します。このとき、W = 3 なら ●◯◯◯●●●◯◯◯●●● のように、最初に 1 個以上、W 個未満の黒マスがある場合を見落とさないように注意が必要です。