$\newcommand{\O}{\mathrm{O}}$
区間加算 $a_i \leftarrow a_i + val (i \in [l, r))$, 区間最小値 $\underset{l \le i < r}{\min} a_i$ のクエリを効率的に処理できる.
区間加算のクエリを要素一つ一つに愚直にやっていると遅くなるが区間がセグ木のノードに対応する区間を含んだ場合そのノードにそれより下に伝播されるべき加算の記録を持っておく.
そしてその後実際に区間最小値のクエリで探索された時に初めてその下に伝播させるようにしてやると $\O (\log n)$ で効率よくクエリを処理できる.
このような処理は遅延評価と呼ばれ, Haskell などの言語で取り入れられているらしい.
以下の実装では高速化のため query 関数を非再帰での実装にしている(非再帰で書かないほうが意味的には理解しやすいが...).
時間計算量: 構築 $\O (n)$, 各クエリ $\O (\log n)$
template<typename T> class segtree { private: int n,sz,h; vector<pair<T, int> > node; vector<T> lazy; void eval(int k) { if(lazy[k]){ node[k].first += lazy[k]; if(k < n) { lazy[k*2] += lazy[k], lazy[k*2+1] += lazy[k]; } lazy[k] = 0; } } public: segtree(const vector<T>& v) : sz((int)v.size()), h(0) { n = 1; while(n < sz) n *= 2, h++; node.resize(2*n, pair<T, int>(numeric_limits<T>::max(), sz)); lazy.resize(2*n, 0); for(int i = 0; i < sz; i++){ node[i+n] = make_pair(v[i], i); } for(int i = n-1; i >= 1; i--){ node[i] = min(node[2*i], node[2*i+1]); } } void range(int a, int b, T x, int k=1, int l=0, int r=-1){ if(r < 0) r = n; eval(k); if(b <= l || r <= a){ return; } if(a <= l && r <= b){ lazy[k] += x; eval(k); }else{ range(a, b, x, 2*k, l, (l+r)/2); range(a, b, x, 2*k+1, (l+r)/2, r); node[k] = min(node[2*k], node[2*k+1]); } } pair<T, int> query(int a, int b) { a += n, b += n - 1; for(int i = h; i > 0; i--) eval(a >> i), eval(b >> i); b++; pair<T, int> res1 = make_pair(numeric_limits<T>::max(), sz); pair<T, int> res2 = make_pair(numeric_limits<T>::max(), sz); while(a < b) { if(a & 1) eval(a), res1 = min(res1, node[a++]); if(b & 1) eval(--b), res2 = min(res2, node[b]); a >>= 1, b >>= 1; } return min(res1, res2); } void print() { for(int i = 0; i < sz; i++){ pair<T,int> p; p = query(i,i+1); cout << "st[" << i << "]: " << p.first << " " << p.second << endl; } } };
AOJ : RMQ and RAQ 提出コード