Processing math: 100%

imos法

配列の長さをNとする.imos法により,区間更新を複数回行った後の,配列の各要素の値を得ることができる.操作の時間計算量は以下.

  • 区間更新: O(1)
  • 配列の各要素の値の取得O(N)

配列の各要素はを成す集合の要素である必要がある.

説明

群における2項演算を仮に和として考える.

imos法でいう区間更新とは,配列のある区間の全要素に同じ値を足す操作のことである.これは,愚直に行うとO(N)かかってしまう.

imos法は,どの区間に何の値を足す必要があるかという情報を覚えておき,配列の要素を最後に取得する時に,それまでの区間更新をまとめて処理するという方法である.より具体的に説明すると,区間更新操作では,左から累積和(O(N))を取った時に,配列の各要素の値を得られるように値の更新を行う.

最後に左から累積和を取ることを前提として考えると,配列のある要素にある値を足すことは,その要素以降の全要素にその値を足すことを意味する.

Index01234567
クエリ1-+1+1+1+1+1+1+1
更新10+1000000
累積和01111111

よって,例えばある区間に値xを足す場合,区間クエリの先頭にxを足し,終わりにxを足すことで,最後に累積和を取った時にその区間の要素のみにxを足すことができる.

Index01234567
クエリ1-+x+x+x+x---
更新10+x000x00
累積和0xxxx000

区間更新が複数あっても操作は変わらない.

Index01234567
クエリ1-+x+x+x+x---
更新10+x000x00
クエリ2--+y+y+y+y--
更新20+x+y00xy0
クエリ3---+z+z---
更新30+x+y+z0xzy0
累積和0xx+yx+y+zx+y+zy00

扱う代数系を群一般としてまとめると,区間[l,r))\(xを作用させる場合,配列のl番目にxを作用させ,r番目にxの逆元を作用させる.実際の値を得るには,左から累積値を取れば良い.

コード

template <class S, S (*op)(S, S), S (*e)(), S (*inv)(S)> struct Imos { private: vector<S> v; public: Imos(int size) { v.assign(size, e()); } void query(int l, int r, S x) { // [l, r) v[l] = op(v[l], x); v[r] = op(v[r], inv(x)); } vector<S> get_values() { rep (i, 1, v.size()) v[i] = op(v[i-1], v[i]); return move(v); } };