SlideShare une entreprise Scribd logo
1  sur  32
Télécharger pour lire hors ligne
ICPC勉強会 2015/01/08
RMQ クエリ処理
北海道大学大学院 情報科学研究科
博士1年 井上 祐馬
1
ICPC勉強会 2015/01/08
• RMQ: Range Minimum Query (区間最小値)
• 列 A[1:n] 上の区間 [l, r] に対するクエリ RMQ(l,r)
• A[l:r] 中での最小値 A[i] を返す
• 基本的に最大値クエリも同様に処理できる
RMQ
2
id 1 2 3 4 5 6
A[id] 1 8 2 6 3 5
l r
ICPC勉強会 2015/01/08
• RMQ: Range Minimum Query (区間最小値)
• 列 A[1:n] 上の区間 [l, r] に対するクエリ RMQ(l,r)
• A[l:r] 中での最小値 A[i] を返す
• 基本的に最大値クエリも同様に処理できる
RMQ
3
id 1 2 3 4 5 6
A[id] 1 8 2 6 3 5
l r
ICPC勉強会 2015/01/08
• RMQ: Range Minimum Query (区間最小値)
• 列 A[1:n] 上の区間 [l, r] に対するクエリ RMQ(l,r)
• A[l:r] 中での最小値 A[i] を返す
• 基本的に最大値クエリも同様に処理できる
RMQ
4
id 1 2 3 4 5 6
A[id] 1 8 2 6 3 5
l r
ICPC勉強会 2015/01/08
RMQを処理するアルゴリズム
• クエリ処理アルゴリズムの計算量:
• 前処理時間
• 1つのクエリの処理時間
5
O(1)O(logn)O(n)
O(n2)
O(nlogn)
O(n)
O( n)
前処理
クエリ処理
ナイーブ

法(1)
O(1)
ナイーブ法(2)
平方

分割
Segment
Tree
Cartesian
Tree + LCA
(割愛)
Sparse Table
ICPC勉強会 2015/01/08
RMQアルゴリズム
• 平方分割
• Segment Tree
• Sparse Table
6
ICPC勉強会 2015/01/08
RMQアルゴリズム
• 平方分割
• Segment Tree
• Sparse Table
7
ICPC勉強会 2015/01/08
バケット分割
• 長さ n の列を長さ s のバケットに分割
• バケットの数 B = n/s
• 各バケットの最小値を O(n) で前計算
• 全体の RMQ クエリは、以下のクエリに分割される
• バケットの区間に対する RMQ クエリ1回
• バケット内部に対する RMQ クエリ2回
8
id 1 2 3 4 5 6 7 8 9 101112131415161718192021222324252627282930
a[1:n] 1 2 1 3 4 3 5 3 6 3 1 8 4 9 11 4 1 5 6 3 2 5 2 5 2 1 9 8 4 1
b[1:B] 1 3 3 1 4 1 2 2 1 1
ICPC勉強会 2015/01/08
平方分割
• 長さ n の列を長さ n のバケットに分割
• バケットの数 B = n/ n = n
• 全体の RMQ クエリは、以下のクエリに分割される
• バケットの区間に対する RMQ クエリ1回

- n個の最小値インデックスを比較 O( n)
• バケット内部に対する RMQ クエリ2回

- n個の最小値インデックスを比較 O( n)
• 全体でも O( n)
9
id 1 2 3 4 5 6 7 8 9 101112131415161718192021222324252627282930
a[1:n] 1 2 1 3 4 3 5 3 6 3 1 8 4 9 11 4 1 5 6 3 2 5 2 5 2 1 9 8 4 1
b[1: n] 1 3 1 1 2 1
n
ICPC勉強会 2015/01/08
平方分割の実装 (構築)
10
class Backet{
//sqrtN: backet size, B: the number of backet
int n, sqrtN, B;
vector<int> val, backet;
public:
Backet(vector<int> a):val(a){
n = a.size(); sqrtN = ceil(sqrt(n));
B = (n + sqrtN -1) / sqrtN;
backet.resize(B,INF);
for(int i=0;i<B;i++){
for(int j=sqrtN*i;j<sqrtN*(i+1);j++){
if(j>=n)break;
backet[i] = min(backet[i], val[j]);
}
}
}
ICPC勉強会 2015/01/08
平方分割の実装 (クエリ)
11
int RMQ(int l, int r){
//index in backet
int L = (l + sqrtN -1) / sqrtN + 1, R = r / sqrtN;
int res = INF;
if(L<=R){
for(int i=l;i<sqrtN*L;i++)res = min(res, val[i]);
for(int i=L;i<R;i++)res = min(res, backet[i]);
for(int i=sqrtN*R;i<r;i++)res = min(res, val[i]);
}else{
//interval is included in one backet
for(int i=l;i<r;i++)res = min(res, val[i]);
}
return res;
}
ICPC勉強会 2015/01/08
RMQアルゴリズム
• 平方分割
• Segment Tree
• Sparse Table
12
ICPC勉強会 2015/01/08
Segment Tree
• 列を2分木の葉に割り当てる
• 各節点は2つの子のうち最小値を保持
• 一見 O(nlogn) 空間に見えて、実は 2n-1 節点
13
a[1:n] 1 2 1 3 4 3 5 3 6 7 1 8 4 - - -
1 1 3 3 6 1 4 -
1 3 1 4
1 1
1
高さ
logn
ICPC勉強会 2015/01/08
Segment Tree
• 列を2分木の葉に割り当てる
• 各節点は2つの子のうち最小値を保持
• 構築:葉の方から順に、2つの子の最小値を計算 O(n)
14
a[1:n] 1 2 1 3 4 3 5 3 6 7 1 8 4 - - -
1 1 3 3 6 1 4 -
1 3 1 4
1 1
1
ICPC勉強会 2015/01/08
Segment Tree
• 列を2分木の葉に割り当てる
• 各節点は2つの子のうち最小値を保持
• クエリ:クエリ区間を節点に対応するように分割し、

    その中の最小値を返す O(log n)
15
a[1:n] 1 2 1 3 4 3 5 3 6 7 1 8 4 - - -
1 1 3 3 6 1 4 -
1 3 1 4
1 1
1
ICPC勉強会 2015/01/08
Segment Tree
• 列を2分木の葉に割り当てる
• 各節点は2つの子のうち最小値を保持
• クエリ:クエリ区間を節点に対応するように分割し、

    その中の最小値を返す O(log n)
16
a[1:n] 1 2 1 3 4 3 5 3 6 7 1 8 4 - - -
1 1 3 3 6 1 4 -
1 3 1 4
1 1
1
ICPC勉強会 2015/01/08
Segment Tree
• 列を2分木の葉に割り当てる
• 各節点は2つの子のうち最小値を保持
• クエリ:クエリ区間を節点に対応するように分割し、

    その中の最小値を返す O(log n)
17
a[1:n] 1 2 1 3 4 3 5 3 6 7 1 8 4 - - -
1 1 3 3 6 1 4 -
1 3 1 4
1 1
1
ICPC勉強会 2015/01/08
Segment Tree
• 列を2分木の葉に割り当てる
• 各節点は2つの子のうち最小値を保持
• クエリ:クエリ区間を節点に対応するように分割し、

    その中の最小値を返す O(log n)
18
a[1:n] 1 2 1 3 4 3 5 3 6 7 1 8 4 - - -
1 1 3 3 6 1 4 -
1 3 1 4
1 1
1
ICPC勉強会 2015/01/08
Segment Tree
• 列を2分木の葉に割り当てる
• 各節点は2つの子のうち最小値を保持
• クエリ:クエリ区間を節点に対応するように分割し、

    その中の最小値を返す O(log n)
19
a[1:n] 1 2 1 3 4 3 5 3 6 7 1 8 4 - - -
1 1 3 3 6 1 4 -
1 3 1 4
1 1
1
ICPC勉強会 2015/01/08
Segment Tree
• クエリ:クエリ区間を節点に対応するように分割し、

    その中の最小値を返す O(log n)
• 最初の分割後の下降において、2つの子のうち片方は必ず
区間一致、もしくは範囲外となり、それ以上降りない

= 高々 O(logn) 回の節点参照
20
a[1:n] 1 2 1 3 4 3 5 3 6 7 1 8 4 - - -
1 1 3 3 6 1 4 -
1 3 1 4
1 1
1
ICPC勉強会 2015/01/08
Segment Tree の実装 (構築)
21
class SegmentTree{
int n;
//represent tree as array
//parent of k : (k-1)/2, child of k : 2*k+1, 2*k+2
vector<int> node;
public:
SegmentTree(vector<int> a){
int n_ = a.size();
//round to power of 2
n=1;
while(n<n_)n*=2;
//initialize tree
node.resize(2*n-1, INF);
//fill data into tree
for(int i=0;i<n_;i++)node[n-1+i] = a[i];
for(int i=n-2;i>=0;i--)node[i] = min(node[2*i+1], node[2*i+2]);
}
ICPC勉強会 2015/01/08
Segment Tree の実装 (クエリ)
22
//return minimum value in [a,b). ( [l,r) is interval in which k is.)
int RMQ(int a,int b,int k=0,int l=0,int r=0){
if(l>=r)r = n;
if(r<=a || b<=l)return INF; //out of range
if(a<=l && r<=b)return node[k]; //interval match
int vl = RMQ(a,b,2*k+1,l,(l+r)/2);
int vr = RMQ(a,b,2*k+2,(l+r)/2,r);
return min(vl,vr);
}
ICPC勉強会 2015/01/08
RMQアルゴリズム
• 平方分割
• Segment Tree
• Sparse Table
23
ICPC勉強会 2015/01/08
Sparse Table
• i 番目から長さ2kの区間の最小値をそれぞれ記憶した
O(n logn)のテーブルを持つ
• 構築: O(n logn)
• S[i][k+1] = min( S[i][k], S[i+2k][k] )
24
i 1 2 3 4 5 6 7 8
A[i] 2 1 8 3 7 2 5 6
S[i][0] 2 1 8 3 7 2 5 6
S[i][1] 1 1 3 3 2 2 5 -
s[i][2] 1 1 2 2 5 - - -
S[i][3] 1 - - - - - - -
ICPC勉強会 2015/01/08
i 1 2 3 4 5 6 7 8
A[i] 2 1 8 3 7 2 5 6
S[i][0] 2 1 8 3 7 2 5 6
S[i][1] 1 1 3 3 2 2 5 -
s[i][2] 1 1 2 2 5 - - -
S[i][3] 1 - - - - - - -
Sparse Table
• i 番目から長さ2kの区間の最小値をそれぞれ記憶した
O(n logn)のテーブルを持つ
• クエリ: O(1)
• RMQ(i, j) = min( S[i][k], S[j-2k+1][k] )
• k = floor( log(j-i) ) ← O(1)で求められると仮定
25
ICPC勉強会 2015/01/08
Sparse Table の実装
26
class SparseTable{
vector< vector<int> > table;
public:
SparseTable(vector<int> a){
int n = a.size(), logn = 31 - __builtin_clz(n);
//initialize table
table.resize(n,vector<int>(logn+1,INF));
for(int i=0;i<n;i++)table[i][0] = a[i];
//construct table
for(int j=0;j<logn;j++){
for(int i=0;i<n;i++){
table[i][j+1] = min(table[i][j], (i+(1<<j)<n)?table[i+(1<<j)][j]:INF);
}
}
}
//return the minimum value in [l, r)
int RMQ(int l, int r){
int ln = 31 - __builtin_clz(r-l);
return min(table[l][ln], table[r-(1<<ln)][ln]);
}
};
ICPC勉強会 2015/01/08
RMQのアルゴリズム
27
空間 前処理 クエリ 動的
平方分割 O(n) O(n) O( n)
○

( O( n) )
Segment
Tree
O(n) O(n) O(logn)
○
( O(logn) )
Sparse
Table
O(n logn) O(n logn) O(1)


( O(n) )
ICPC勉強会 2015/01/08
動的クエリ処理
• 値変更クエリ
• セグメント木:葉の値の変更を親に伝える O(logn)
• 平方分割:バケット最小値を更新 O( n)
• 値挿入クエリ
• セグメント木:クエリの先読みができれば先に な
どで確保しておく
• 平方分割:バケットに追加、サイズが大きくなり
すぎたら分割 O( n)
28
ICPC勉強会 2015/01/08
Segment Tree の実装 (更新)
29
void update(int k, int v){
k += n-1;
node[k] = v;
//traverse from leaf to root
while(k>0){
k = (k-1)/2;
node[k] = min(node[k*2+1],node[k*2+2]);
}
}
ICPC勉強会 2015/01/08
平方分割の実装 (更新)
30
void update(int k, int v){
//update of array
val[k] = v;
//update of backet
int X = k/sqrtN;
backet[X] = INF;
for(int i=sqrtN*X;i<sqrtN*(X+1);i++){
if(i>=n)break;
backet[X] = min(backet[X], val[i]);
}
}
ICPC勉強会 2015/01/08
その他クエリ処理
• セグメント木 (平方分割) は他にも様々な問題に利用可能
• 区間和/積、区間GCD/LCM、etc...
• 結合則が成り立つ演算なら基本的にOK
• 区間和に対するクエリに特化した BIT (Binary Index Tree
= Fenwick Tree) という構造もある (そのうち紹介)
• 1次元 (列) を多次元に拡張することもできる (そのうち紹介)
• 遅延更新というテクニックで、さらに幅広いクエリを処理
できる (そのうち紹介)
31
ICPC勉強会 2015/01/08
まとめ
• RMQ を処理する典型的アルゴリズムの紹介
• 平方分割
• セグメント木
• Sparse Table
• (一部は) 動的なデータの変更にも対応できる
• これらのアルゴリズム (データ構造) は他の問題に
も幅広く応用される
32

Contenu connexe

Tendances

プログラミングコンテストでの乱択アルゴリズム
プログラミングコンテストでの乱択アルゴリズムプログラミングコンテストでの乱択アルゴリズム
プログラミングコンテストでの乱択アルゴリズム
Takuya Akiba
 
プログラミングコンテストでのデータ構造
プログラミングコンテストでのデータ構造プログラミングコンテストでのデータ構造
プログラミングコンテストでのデータ構造
Takuya Akiba
 

Tendances (20)

プログラミングコンテストでの乱択アルゴリズム
プログラミングコンテストでの乱択アルゴリズムプログラミングコンテストでの乱択アルゴリズム
プログラミングコンテストでの乱択アルゴリズム
 
プログラミングコンテストでのデータ構造 2 ~動的木編~
プログラミングコンテストでのデータ構造 2 ~動的木編~プログラミングコンテストでのデータ構造 2 ~動的木編~
プログラミングコンテストでのデータ構造 2 ~動的木編~
 
動的計画法を極める!
動的計画法を極める!動的計画法を極める!
動的計画法を極める!
 
AtCoder Beginner Contest 023 解説
AtCoder Beginner Contest 023 解説AtCoder Beginner Contest 023 解説
AtCoder Beginner Contest 023 解説
 
AtCoder Beginner Contest 017 解説
AtCoder Beginner Contest 017 解説AtCoder Beginner Contest 017 解説
AtCoder Beginner Contest 017 解説
 
プログラミングコンテストでのデータ構造
プログラミングコンテストでのデータ構造プログラミングコンテストでのデータ構造
プログラミングコンテストでのデータ構造
 
AtCoder Beginner Contest 015 解説
AtCoder Beginner Contest 015 解説AtCoder Beginner Contest 015 解説
AtCoder Beginner Contest 015 解説
 
ウェーブレット木の世界
ウェーブレット木の世界ウェーブレット木の世界
ウェーブレット木の世界
 
色々なダイクストラ高速化
色々なダイクストラ高速化色々なダイクストラ高速化
色々なダイクストラ高速化
 
CODE FESTIVAL 2015 予選A 解説
CODE FESTIVAL 2015 予選A 解説CODE FESTIVAL 2015 予選A 解説
CODE FESTIVAL 2015 予選A 解説
 
arc047
arc047arc047
arc047
 
双対性
双対性双対性
双対性
 
グラフネットワーク〜フロー&カット〜
グラフネットワーク〜フロー&カット〜グラフネットワーク〜フロー&カット〜
グラフネットワーク〜フロー&カット〜
 
Convex Hull Trick
Convex Hull TrickConvex Hull Trick
Convex Hull Trick
 
ユークリッド最小全域木
ユークリッド最小全域木ユークリッド最小全域木
ユークリッド最小全域木
 
Rolling Hashを殺す話
Rolling Hashを殺す話Rolling Hashを殺す話
Rolling Hashを殺す話
 
写像 12 相
写像 12 相写像 12 相
写像 12 相
 
Union find(素集合データ構造)
Union find(素集合データ構造)Union find(素集合データ構造)
Union find(素集合データ構造)
 
Re永続データ構造が分からない人のためのスライド
Re永続データ構造が分からない人のためのスライドRe永続データ構造が分からない人のためのスライド
Re永続データ構造が分からない人のためのスライド
 
明日使えないすごいビット演算
明日使えないすごいビット演算明日使えないすごいビット演算
明日使えないすごいビット演算
 

Similaire à RMQ クエリ処理

Ibisml2011 06-20
Ibisml2011 06-20Ibisml2011 06-20
Ibisml2011 06-20
Yasuo Tabei
 
Cvim tutorial2 03_06_wk77_110220-0546
Cvim tutorial2 03_06_wk77_110220-0546Cvim tutorial2 03_06_wk77_110220-0546
Cvim tutorial2 03_06_wk77_110220-0546
Wataru Kishimoto
 
Reactive Webアプリケーション - そしてSpring 5へ #jjug_ccc #ccc_ef3
Reactive Webアプリケーション - そしてSpring 5へ #jjug_ccc #ccc_ef3Reactive Webアプリケーション - そしてSpring 5へ #jjug_ccc #ccc_ef3
Reactive Webアプリケーション - そしてSpring 5へ #jjug_ccc #ccc_ef3
Toshiaki Maki
 
Ruby科学データ処理ツールの開発 NArrayとPwrake
Ruby科学データ処理ツールの開発 NArrayとPwrakeRuby科学データ処理ツールの開発 NArrayとPwrake
Ruby科学データ処理ツールの開発 NArrayとPwrake
Masahiro Tanaka
 

Similaire à RMQ クエリ処理 (20)

Ibisml2011 06-20
Ibisml2011 06-20Ibisml2011 06-20
Ibisml2011 06-20
 
Cvim tutorial2 03_06_wk77_110220-0546
Cvim tutorial2 03_06_wk77_110220-0546Cvim tutorial2 03_06_wk77_110220-0546
Cvim tutorial2 03_06_wk77_110220-0546
 
200514material minami
200514material minami200514material minami
200514material minami
 
各言語の k-means 比較
各言語の k-means 比較各言語の k-means 比較
各言語の k-means 比較
 
アルゴリズムイントロダクション15章 動的計画法
アルゴリズムイントロダクション15章 動的計画法アルゴリズムイントロダクション15章 動的計画法
アルゴリズムイントロダクション15章 動的計画法
 
Tokyo r27
Tokyo r27Tokyo r27
Tokyo r27
 
論文紹介:The wavelet matrix
論文紹介:The wavelet matrix論文紹介:The wavelet matrix
論文紹介:The wavelet matrix
 
文字列カーネルによる辞書なしツイート分類 〜文字列カーネル入門〜
文字列カーネルによる辞書なしツイート分類 〜文字列カーネル入門〜文字列カーネルによる辞書なしツイート分類 〜文字列カーネル入門〜
文字列カーネルによる辞書なしツイート分類 〜文字列カーネル入門〜
 
Reactive Webアプリケーション - そしてSpring 5へ #jjug_ccc #ccc_ef3
Reactive Webアプリケーション - そしてSpring 5へ #jjug_ccc #ccc_ef3Reactive Webアプリケーション - そしてSpring 5へ #jjug_ccc #ccc_ef3
Reactive Webアプリケーション - そしてSpring 5へ #jjug_ccc #ccc_ef3
 
Ruby科学データ処理ツールの開発 NArrayとPwrake
Ruby科学データ処理ツールの開発 NArrayとPwrakeRuby科学データ処理ツールの開発 NArrayとPwrake
Ruby科学データ処理ツールの開発 NArrayとPwrake
 
HiRoshimaR3_IntroR
HiRoshimaR3_IntroRHiRoshimaR3_IntroR
HiRoshimaR3_IntroR
 
STARC RTL設計スタイルガイドによるVerilog HDL並列記述の補強
STARC RTL設計スタイルガイドによるVerilog HDL並列記述の補強STARC RTL設計スタイルガイドによるVerilog HDL並列記述の補強
STARC RTL設計スタイルガイドによるVerilog HDL並列記述の補強
 
ipsjifat201909
ipsjifat201909ipsjifat201909
ipsjifat201909
 
レコードリンケージに基づく科研費分野-WoS分野マッピングの導出
レコードリンケージに基づく科研費分野-WoS分野マッピングの導出レコードリンケージに基づく科研費分野-WoS分野マッピングの導出
レコードリンケージに基づく科研費分野-WoS分野マッピングの導出
 
2010 in-depth-v11
2010 in-depth-v112010 in-depth-v11
2010 in-depth-v11
 
検索評価ツールキットNTCIREVALを用いた様々な情報アクセス技術の評価方法
検索評価ツールキットNTCIREVALを用いた様々な情報アクセス技術の評価方法検索評価ツールキットNTCIREVALを用いた様々な情報アクセス技術の評価方法
検索評価ツールキットNTCIREVALを用いた様々な情報アクセス技術の評価方法
 
Rによるデータサイエンス:12章「時系列」
Rによるデータサイエンス:12章「時系列」Rによるデータサイエンス:12章「時系列」
Rによるデータサイエンス:12章「時系列」
 
ソフトウェア自動チューニング研究紹介
ソフトウェア自動チューニング研究紹介ソフトウェア自動チューニング研究紹介
ソフトウェア自動チューニング研究紹介
 
BigQuery勉強会 Standard SQL Dialect
BigQuery勉強会 Standard SQL DialectBigQuery勉強会 Standard SQL Dialect
BigQuery勉強会 Standard SQL Dialect
 
Nagoya.R #12 入門者講習
Nagoya.R #12 入門者講習Nagoya.R #12 入門者講習
Nagoya.R #12 入門者講習
 

Plus de HCPC: 北海道大学競技プログラミングサークル

Plus de HCPC: 北海道大学競技プログラミングサークル (20)

ACPC 2017 Day3 F: 掛け算は楽しい
ACPC 2017 Day3 F: 掛け算は楽しいACPC 2017 Day3 F: 掛け算は楽しい
ACPC 2017 Day3 F: 掛け算は楽しい
 
ACPC 2017 Day3 D: 優柔不断
ACPC 2017 Day3 D: 優柔不断ACPC 2017 Day3 D: 優柔不断
ACPC 2017 Day3 D: 優柔不断
 
ACPC 2019 Day3 G: Restricted DFS
ACPC 2019 Day3 G: Restricted DFSACPC 2019 Day3 G: Restricted DFS
ACPC 2019 Day3 G: Restricted DFS
 
ACPC 2019 Day3 F: 部分文字列分解
ACPC 2019 Day3 F: 部分文字列分解ACPC 2019 Day3 F: 部分文字列分解
ACPC 2019 Day3 F: 部分文字列分解
 
ACPC 2019 Day3 E: 総和の切り取り
ACPC 2019 Day3 E: 総和の切り取りACPC 2019 Day3 E: 総和の切り取り
ACPC 2019 Day3 E: 総和の切り取り
 
ACPC 2019 Day3 B: パフェ
ACPC 2019 Day3 B: パフェACPC 2019 Day3 B: パフェ
ACPC 2019 Day3 B: パフェ
 
ACPC 2019 Day3 A: 間違い探し
ACPC 2019 Day3 A: 間違い探しACPC 2019 Day3 A: 間違い探し
ACPC 2019 Day3 A: 間違い探し
 
HUPC 2019 Day2 G: 木
HUPC 2019 Day2 G: 木HUPC 2019 Day2 G: 木
HUPC 2019 Day2 G: 木
 
HUPC 2019 Day2 E: ジャム
HUPC 2019 Day2 E: ジャムHUPC 2019 Day2 E: ジャム
HUPC 2019 Day2 E: ジャム
 
HUPC 2019 Day2 H: Revenge of UMG
HUPC 2019 Day2 H: Revenge of UMGHUPC 2019 Day2 H: Revenge of UMG
HUPC 2019 Day2 H: Revenge of UMG
 
HUPC 2019 Day2 F: MOD Rush
HUPC 2019 Day2 F: MOD RushHUPC 2019 Day2 F: MOD Rush
HUPC 2019 Day2 F: MOD Rush
 
HUPC 2019 Day2 C: 串刺し
HUPC 2019 Day2 C: 串刺しHUPC 2019 Day2 C: 串刺し
HUPC 2019 Day2 C: 串刺し
 
HUPC 2019 Day1 F: グリッドの番号
HUPC 2019 Day1 F: グリッドの番号HUPC 2019 Day1 F: グリッドの番号
HUPC 2019 Day1 F: グリッドの番号
 
HUPC 2019 Day1 E: 最短経路の復元
HUPC 2019 Day1 E: 最短経路の復元HUPC 2019 Day1 E: 最短経路の復元
HUPC 2019 Day1 E: 最短経路の復元
 
HUPC 2019 Day1 D: 貪欲が最適?
HUPC 2019 Day1 D: 貪欲が最適?HUPC 2019 Day1 D: 貪欲が最適?
HUPC 2019 Day1 D: 貪欲が最適?
 
HUPC 2019 Day1 C: 短絡評価
HUPC 2019 Day1 C: 短絡評価HUPC 2019 Day1 C: 短絡評価
HUPC 2019 Day1 C: 短絡評価
 
HUPC 2019 Day1 B: 自身の 2 倍
HUPC 2019 Day1 B: 自身の 2 倍HUPC 2019 Day1 B: 自身の 2 倍
HUPC 2019 Day1 B: 自身の 2 倍
 
HUPC 2019 Day1 A: four tea
HUPC 2019 Day1 A: four teaHUPC 2019 Day1 A: four tea
HUPC 2019 Day1 A: four tea
 
プログラミングコンテスト基礎テクニック
プログラミングコンテスト基礎テクニックプログラミングコンテスト基礎テクニック
プログラミングコンテスト基礎テクニック
 
RUPC 2019 Day3 G: Donuts Orientation
RUPC 2019 Day3 G: Donuts OrientationRUPC 2019 Day3 G: Donuts Orientation
RUPC 2019 Day3 G: Donuts Orientation
 

RMQ クエリ処理

  • 1. ICPC勉強会 2015/01/08 RMQ クエリ処理 北海道大学大学院 情報科学研究科 博士1年 井上 祐馬 1
  • 2. ICPC勉強会 2015/01/08 • RMQ: Range Minimum Query (区間最小値) • 列 A[1:n] 上の区間 [l, r] に対するクエリ RMQ(l,r) • A[l:r] 中での最小値 A[i] を返す • 基本的に最大値クエリも同様に処理できる RMQ 2 id 1 2 3 4 5 6 A[id] 1 8 2 6 3 5 l r
  • 3. ICPC勉強会 2015/01/08 • RMQ: Range Minimum Query (区間最小値) • 列 A[1:n] 上の区間 [l, r] に対するクエリ RMQ(l,r) • A[l:r] 中での最小値 A[i] を返す • 基本的に最大値クエリも同様に処理できる RMQ 3 id 1 2 3 4 5 6 A[id] 1 8 2 6 3 5 l r
  • 4. ICPC勉強会 2015/01/08 • RMQ: Range Minimum Query (区間最小値) • 列 A[1:n] 上の区間 [l, r] に対するクエリ RMQ(l,r) • A[l:r] 中での最小値 A[i] を返す • 基本的に最大値クエリも同様に処理できる RMQ 4 id 1 2 3 4 5 6 A[id] 1 8 2 6 3 5 l r
  • 5. ICPC勉強会 2015/01/08 RMQを処理するアルゴリズム • クエリ処理アルゴリズムの計算量: • 前処理時間 • 1つのクエリの処理時間 5 O(1)O(logn)O(n) O(n2) O(nlogn) O(n) O( n) 前処理 クエリ処理 ナイーブ
 法(1) O(1) ナイーブ法(2) 平方
 分割 Segment Tree Cartesian Tree + LCA (割愛) Sparse Table
  • 8. ICPC勉強会 2015/01/08 バケット分割 • 長さ n の列を長さ s のバケットに分割 • バケットの数 B = n/s • 各バケットの最小値を O(n) で前計算 • 全体の RMQ クエリは、以下のクエリに分割される • バケットの区間に対する RMQ クエリ1回 • バケット内部に対する RMQ クエリ2回 8 id 1 2 3 4 5 6 7 8 9 101112131415161718192021222324252627282930 a[1:n] 1 2 1 3 4 3 5 3 6 3 1 8 4 9 11 4 1 5 6 3 2 5 2 5 2 1 9 8 4 1 b[1:B] 1 3 3 1 4 1 2 2 1 1
  • 9. ICPC勉強会 2015/01/08 平方分割 • 長さ n の列を長さ n のバケットに分割 • バケットの数 B = n/ n = n • 全体の RMQ クエリは、以下のクエリに分割される • バケットの区間に対する RMQ クエリ1回
 - n個の最小値インデックスを比較 O( n) • バケット内部に対する RMQ クエリ2回
 - n個の最小値インデックスを比較 O( n) • 全体でも O( n) 9 id 1 2 3 4 5 6 7 8 9 101112131415161718192021222324252627282930 a[1:n] 1 2 1 3 4 3 5 3 6 3 1 8 4 9 11 4 1 5 6 3 2 5 2 5 2 1 9 8 4 1 b[1: n] 1 3 1 1 2 1 n
  • 10. ICPC勉強会 2015/01/08 平方分割の実装 (構築) 10 class Backet{ //sqrtN: backet size, B: the number of backet int n, sqrtN, B; vector<int> val, backet; public: Backet(vector<int> a):val(a){ n = a.size(); sqrtN = ceil(sqrt(n)); B = (n + sqrtN -1) / sqrtN; backet.resize(B,INF); for(int i=0;i<B;i++){ for(int j=sqrtN*i;j<sqrtN*(i+1);j++){ if(j>=n)break; backet[i] = min(backet[i], val[j]); } } }
  • 11. ICPC勉強会 2015/01/08 平方分割の実装 (クエリ) 11 int RMQ(int l, int r){ //index in backet int L = (l + sqrtN -1) / sqrtN + 1, R = r / sqrtN; int res = INF; if(L<=R){ for(int i=l;i<sqrtN*L;i++)res = min(res, val[i]); for(int i=L;i<R;i++)res = min(res, backet[i]); for(int i=sqrtN*R;i<r;i++)res = min(res, val[i]); }else{ //interval is included in one backet for(int i=l;i<r;i++)res = min(res, val[i]); } return res; }
  • 13. ICPC勉強会 2015/01/08 Segment Tree • 列を2分木の葉に割り当てる • 各節点は2つの子のうち最小値を保持 • 一見 O(nlogn) 空間に見えて、実は 2n-1 節点 13 a[1:n] 1 2 1 3 4 3 5 3 6 7 1 8 4 - - - 1 1 3 3 6 1 4 - 1 3 1 4 1 1 1 高さ logn
  • 14. ICPC勉強会 2015/01/08 Segment Tree • 列を2分木の葉に割り当てる • 各節点は2つの子のうち最小値を保持 • 構築:葉の方から順に、2つの子の最小値を計算 O(n) 14 a[1:n] 1 2 1 3 4 3 5 3 6 7 1 8 4 - - - 1 1 3 3 6 1 4 - 1 3 1 4 1 1 1
  • 15. ICPC勉強会 2015/01/08 Segment Tree • 列を2分木の葉に割り当てる • 各節点は2つの子のうち最小値を保持 • クエリ:クエリ区間を節点に対応するように分割し、
     その中の最小値を返す O(log n) 15 a[1:n] 1 2 1 3 4 3 5 3 6 7 1 8 4 - - - 1 1 3 3 6 1 4 - 1 3 1 4 1 1 1
  • 16. ICPC勉強会 2015/01/08 Segment Tree • 列を2分木の葉に割り当てる • 各節点は2つの子のうち最小値を保持 • クエリ:クエリ区間を節点に対応するように分割し、
     その中の最小値を返す O(log n) 16 a[1:n] 1 2 1 3 4 3 5 3 6 7 1 8 4 - - - 1 1 3 3 6 1 4 - 1 3 1 4 1 1 1
  • 17. ICPC勉強会 2015/01/08 Segment Tree • 列を2分木の葉に割り当てる • 各節点は2つの子のうち最小値を保持 • クエリ:クエリ区間を節点に対応するように分割し、
     その中の最小値を返す O(log n) 17 a[1:n] 1 2 1 3 4 3 5 3 6 7 1 8 4 - - - 1 1 3 3 6 1 4 - 1 3 1 4 1 1 1
  • 18. ICPC勉強会 2015/01/08 Segment Tree • 列を2分木の葉に割り当てる • 各節点は2つの子のうち最小値を保持 • クエリ:クエリ区間を節点に対応するように分割し、
     その中の最小値を返す O(log n) 18 a[1:n] 1 2 1 3 4 3 5 3 6 7 1 8 4 - - - 1 1 3 3 6 1 4 - 1 3 1 4 1 1 1
  • 19. ICPC勉強会 2015/01/08 Segment Tree • 列を2分木の葉に割り当てる • 各節点は2つの子のうち最小値を保持 • クエリ:クエリ区間を節点に対応するように分割し、
     その中の最小値を返す O(log n) 19 a[1:n] 1 2 1 3 4 3 5 3 6 7 1 8 4 - - - 1 1 3 3 6 1 4 - 1 3 1 4 1 1 1
  • 20. ICPC勉強会 2015/01/08 Segment Tree • クエリ:クエリ区間を節点に対応するように分割し、
     その中の最小値を返す O(log n) • 最初の分割後の下降において、2つの子のうち片方は必ず 区間一致、もしくは範囲外となり、それ以上降りない
 = 高々 O(logn) 回の節点参照 20 a[1:n] 1 2 1 3 4 3 5 3 6 7 1 8 4 - - - 1 1 3 3 6 1 4 - 1 3 1 4 1 1 1
  • 21. ICPC勉強会 2015/01/08 Segment Tree の実装 (構築) 21 class SegmentTree{ int n; //represent tree as array //parent of k : (k-1)/2, child of k : 2*k+1, 2*k+2 vector<int> node; public: SegmentTree(vector<int> a){ int n_ = a.size(); //round to power of 2 n=1; while(n<n_)n*=2; //initialize tree node.resize(2*n-1, INF); //fill data into tree for(int i=0;i<n_;i++)node[n-1+i] = a[i]; for(int i=n-2;i>=0;i--)node[i] = min(node[2*i+1], node[2*i+2]); }
  • 22. ICPC勉強会 2015/01/08 Segment Tree の実装 (クエリ) 22 //return minimum value in [a,b). ( [l,r) is interval in which k is.) int RMQ(int a,int b,int k=0,int l=0,int r=0){ if(l>=r)r = n; if(r<=a || b<=l)return INF; //out of range if(a<=l && r<=b)return node[k]; //interval match int vl = RMQ(a,b,2*k+1,l,(l+r)/2); int vr = RMQ(a,b,2*k+2,(l+r)/2,r); return min(vl,vr); }
  • 24. ICPC勉強会 2015/01/08 Sparse Table • i 番目から長さ2kの区間の最小値をそれぞれ記憶した O(n logn)のテーブルを持つ • 構築: O(n logn) • S[i][k+1] = min( S[i][k], S[i+2k][k] ) 24 i 1 2 3 4 5 6 7 8 A[i] 2 1 8 3 7 2 5 6 S[i][0] 2 1 8 3 7 2 5 6 S[i][1] 1 1 3 3 2 2 5 - s[i][2] 1 1 2 2 5 - - - S[i][3] 1 - - - - - - -
  • 25. ICPC勉強会 2015/01/08 i 1 2 3 4 5 6 7 8 A[i] 2 1 8 3 7 2 5 6 S[i][0] 2 1 8 3 7 2 5 6 S[i][1] 1 1 3 3 2 2 5 - s[i][2] 1 1 2 2 5 - - - S[i][3] 1 - - - - - - - Sparse Table • i 番目から長さ2kの区間の最小値をそれぞれ記憶した O(n logn)のテーブルを持つ • クエリ: O(1) • RMQ(i, j) = min( S[i][k], S[j-2k+1][k] ) • k = floor( log(j-i) ) ← O(1)で求められると仮定 25
  • 26. ICPC勉強会 2015/01/08 Sparse Table の実装 26 class SparseTable{ vector< vector<int> > table; public: SparseTable(vector<int> a){ int n = a.size(), logn = 31 - __builtin_clz(n); //initialize table table.resize(n,vector<int>(logn+1,INF)); for(int i=0;i<n;i++)table[i][0] = a[i]; //construct table for(int j=0;j<logn;j++){ for(int i=0;i<n;i++){ table[i][j+1] = min(table[i][j], (i+(1<<j)<n)?table[i+(1<<j)][j]:INF); } } } //return the minimum value in [l, r) int RMQ(int l, int r){ int ln = 31 - __builtin_clz(r-l); return min(table[l][ln], table[r-(1<<ln)][ln]); } };
  • 27. ICPC勉強会 2015/01/08 RMQのアルゴリズム 27 空間 前処理 クエリ 動的 平方分割 O(n) O(n) O( n) ○
 ( O( n) ) Segment Tree O(n) O(n) O(logn) ○ ( O(logn) ) Sparse Table O(n logn) O(n logn) O(1) 
 ( O(n) )
  • 28. ICPC勉強会 2015/01/08 動的クエリ処理 • 値変更クエリ • セグメント木:葉の値の変更を親に伝える O(logn) • 平方分割:バケット最小値を更新 O( n) • 値挿入クエリ • セグメント木:クエリの先読みができれば先に な どで確保しておく • 平方分割:バケットに追加、サイズが大きくなり すぎたら分割 O( n) 28
  • 29. ICPC勉強会 2015/01/08 Segment Tree の実装 (更新) 29 void update(int k, int v){ k += n-1; node[k] = v; //traverse from leaf to root while(k>0){ k = (k-1)/2; node[k] = min(node[k*2+1],node[k*2+2]); } }
  • 30. ICPC勉強会 2015/01/08 平方分割の実装 (更新) 30 void update(int k, int v){ //update of array val[k] = v; //update of backet int X = k/sqrtN; backet[X] = INF; for(int i=sqrtN*X;i<sqrtN*(X+1);i++){ if(i>=n)break; backet[X] = min(backet[X], val[i]); } }
  • 31. ICPC勉強会 2015/01/08 その他クエリ処理 • セグメント木 (平方分割) は他にも様々な問題に利用可能 • 区間和/積、区間GCD/LCM、etc... • 結合則が成り立つ演算なら基本的にOK • 区間和に対するクエリに特化した BIT (Binary Index Tree = Fenwick Tree) という構造もある (そのうち紹介) • 1次元 (列) を多次元に拡張することもできる (そのうち紹介) • 遅延更新というテクニックで、さらに幅広いクエリを処理 できる (そのうち紹介) 31
  • 32. ICPC勉強会 2015/01/08 まとめ • RMQ を処理する典型的アルゴリズムの紹介 • 平方分割 • セグメント木 • Sparse Table • (一部は) 動的なデータの変更にも対応できる • これらのアルゴリズム (データ構造) は他の問題に も幅広く応用される 32