🎯
【Java】いもす法
いもす法
いもす法とは、0
で初期化された配列の指定した区間の要素に同じ数を足した配列を求める時に、累積和を用いることにより高速に求めることができるアルゴリズムです。
また、今までは配列a
に対して累積和の配列s
を用意していましたが、今回は配列a
を累積和の配列に変換することで求めたい配列を用意します。
そして、累積和の配列において要素数0個を考慮しません。
いもす法のアルゴリズム
- サイズ
n+1
の配列a
を0
で初期化。 ※サイズをn+1
とするのは、a[r+1] -= x
を行うため。 -
q
回にわたって区間l
→r
にx
を足す。-
q
回、a[l]
にx
を、a[r+1]
に-x
を加算します。-
i
を0
からq-1
までループするfor
文を定義して、 -
a[l] += x
,a[r+1] -= x
を加算します。
-
- 配列
a
を累積和の配列にする。-
i
を1
からn-1
までループするfor
文を定義して、 -
a[i] += a[i-1]
と更新します。
-
-
a[l]
にx
を、a[r+1]
に-x
を加算する理由
- これまでの学習で、累積和の配列
s
のi+1
番目とi
番目の差が、元の配列a
の要素a[i]
であることはわかっています。これを一般化すると下記の通りになります。s[i+1] - s[i] = a[i]
-
次に、累積和の配列
s
の区間[l+1]
→[r+1]
にx
を加えるためには、
累積和の配列s
においてl+1
番目にx
を加えることと、
累積和の配列s
においてr+2
番目にx
を加えた効果が打ち消す必要があります。- 累積和の配列
s
においてl+1
番目にx
を加えることは、下記の通りです。
s[l+1] - s[l] += x,
この式に先の
s[i+1] - s[i] = a[i]
を代入すると、下記の通りになりますa[l] += x
これで
a[l]
にx
を加算 することになりました。- 累積和の配列
s
においてr+2
番目にx
を加えた効果が打ち消すことは、下記の通りです。
s[r+2] - s[r+1] -= x
この式に先の
s[i+1] - s[i] = a[i]
を代入すると、下記の通りになりますa[r+1] -= x
これで
a[r+1]
に-x
を加算 することになりました。 - 累積和の配列
例題
n = 5
q = 3
q1 : l=2, r=3, x=40
q2 : l=0, r=3, x=1
q3 : l=1, r=4, x=5
アルゴリズム1
- サイズ
6
の配列a
を0
で初期化。a = [0, 0, 0, 0, 0, 0]
アルゴリズム2
-
3
回、a[l]
にx
を、a[r+1]
に-x
を加算します。- l=2, r=3, x=40
a = [0, 0, 40, 0, -40, 0]
- l=0, r=3, x=1
a = [1, 0, 40, 0, -41, 0]
- l=1, r=4, x=5
a = [1, 5, 40, 0, -41, -5]
-
a[i] += a[i-1]
を繰り返し、配列a
を累積和の配列にする。a = [1, 6, 46, 46, 5]
コード例
今回もコード例の解説は割愛します。
import java.util.*;
public class Main {
static void printArray(int[] a, int n) {
for (int i = 0; i < n; i++) {
if (i != 0) {
System.out.print(" ");
}
System.out.print(a[i]);
}
System.out.println();
}
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n, q;
n = sc.nextInt();
q = sc.nextInt();
// サイズ n+1 の配列 a を作成
int[] a = new int[n + 1];
//
for (int i = 0; i < q; i++) {
int l, r, x;
l = sc.nextInt();
r = sc.nextInt();
x = sc.nextInt();
// a[l] に x を、a[r + 1] に -x を加算;
a[l] += x;
a[r + 1] -= x;
}
// i を 1 から n-1 までループする
for (int i = 1; i < n; i++) {
// 配列 a を累積和の配列にする
a[i] += a[i - 1];
}
printArray(a, n);
}
}
入力値
5 3
2 3 40
0 3 1
1 4 5
出力結果
1 6 46 46 5
例題の解答と同じになることが分かりました。
Discussion