procon2019
このアカウントは京大マイコンクラブ(KMC)の2019年度の競技プログラミング練習会Normal(初心者向け)での説明用に作成したスライドです。 閲覧・参照はご自由にどうぞ。
第14回
セグメント木、(BIT、)平方分割
担当 : laft
KMC-ID:laft
制約
マグマから群に進むに連れて、条件が厳しくなる。
これはRMQの例。
一番下の行に元の配列を入れておく。
その1つ上の行には、各ノードの2つの子のうち小さい方を入れる。
その1つ上の行には、各ノードの2つの子のうち小さい方を入れる。
その1つ上の行には、各ノードの2つの子のうち小さい方を入れる。
その1つ上の行には、各ノードの2つの子のうち小さい方を入れる。
この結果、例えば緑の値は赤の値の区間minとなる。
この結果、例えば緑の値は赤の値の区間minとなる。
この結果、例えば緑の値は赤の値の区間minとなる。
この性質によってクエリを高速に処理できるようになる。
ex. \(a_5\)から\(a_{13}\)の最小値を求める。
ex. \(a_5\)から\(a_{13}\)の最小値を求める。(0-indexed)
この赤い部分が指定した5~13番目の範囲。
ex. \(a_5\)から\(a_{13}\)の最小値を求める。(0-indexed)
セグ木の性質から、この部分だけで十分。
ex. \(a_5\)から\(a_{13}\)の最小値を求める。(0-indexed)
ex. \(a_5\)から\(a_{13}\)の最小値を求める。(0-indexed)
上から欲しいものを得るまで探索する。
ex. \(a_5\)から\(a_{13}\)の最小値を求める。(0-indexed)
上から欲しいものを得るまで探索する。
ex. \(a_5\)から\(a_{13}\)の最小値を求める。(0-indexed)
上から欲しいものを得るまで探索する。
赤線の外なので探索打ち切り!
ex. \(a_5\)から\(a_{13}\)の最小値を求める。(0-indexed)
上から欲しいものを得るまで探索する。
ex. \(a_5\)から\(a_{13}\)の最小値を求める。(0-indexed)
上から欲しいものを得るまで探索する。
ex. \(a_5\)から\(a_{13}\)の最小値を求める。(0-indexed)
上から欲しいものを得るまで探索する。
赤線の外なので探索打ち切り!
ex. \(a_5\)から\(a_{13}\)の最小値を求める。(0-indexed)
上から欲しいものを得るまで探索する。
ex. \(a_5\)から\(a_{13}\)の最小値を求める。(0-indexed)
上から欲しいものを得るまで探索する。
赤線の内なので必要な解!
探索は打ち切る。
ex. \(a_5\)から\(a_{13}\)の最小値を求める。(0-indexed)
上から欲しいものを得るまで探索する。
ex. \(a_5\)から\(a_{13}\)の最小値を求める。(0-indexed)
上から欲しいものを得るまで探索する。
赤線の内なので必要な解!
探索は打ち切る。
ex. \(a_5\)から\(a_{13}\)の最小値を求める。(0-indexed)
上から欲しいものを得るまで探索する。
ex. \(a_5\)から\(a_{13}\)の最小値を求める。(0-indexed)
上から欲しいものを得るまで探索する。
ex. \(a_5\)から\(a_{13}\)の最小値を求める。(0-indexed)
上から欲しいものを得るまで探索する。
ex. \(a_5\)から\(a_{13}\)の最小値を求める。(0-indexed)
上から欲しいものを得るまで探索する。
ex. \(a_5\)から\(a_{13}\)の最小値を求める。(0-indexed)
上から欲しいものを得るまで探索する。
ex. \(a_5\)から\(a_{13}\)の最小値を求める。(0-indexed)
上から欲しいものを得るまで探索する。
ex. \(a_5\)から\(a_{13}\)の最小値を求める。(0-indexed)
緑のminをとれば十分!!
ex. \(a_3\)を4に書き換える。(0-indexed)
下から順に書き換えていく。
ex. \(a_3\)を4に書き換える。(0-indexed)
下から順に書き換えていく。
ex. \(a_3\)を4に書き換える。(0-indexed)
下から順に書き換えていく。
ex. \(a_3\)を4に書き換える。(0-indexed)
下から順に書き換えていく。
ex. \(a_3\)を4に書き換える。(0-indexed)
下から順に書き換えていく。
ex. \(a_3\)を4に書き換える。(0-indexed)
下から順に書き換えていく。
完全二分木を作るために、最初に\(N\)以上の2べき(\(2^x\))のサイズだったことにする。
完全二分木は配列で管理できる!
そこで、下のようにindexを振ることにする。
親から子へのアクセス。
親のindexをnとすると、2*n+1, 2*n+2でそれぞれの子へアクセスできる。
逆に、子から親へのアクセス。
子のindexをnとすると、
\(\lfloor\frac{n-1}{2}\rfloor\)でアクセスできる。
struct rmq{
int n; //size
vector<int> segtree; //完全二分木を格納した配列
const int INF = INT_MAX;
rmq(int sz){
n = 1;
while(n<sz) n<<=1; //配列サイズをsz以上の2冪に。
//セグ木のサイズは2*n-1, 取り敢えず全て単位元で初期化しておく。
segtree.assign(2*n-1,INF);
}
int find(int l, int r){
//後で
}
void update(int i, int x){
//後で
}
};
struct rmq{
int n; //size
vector<int> segtree; //完全二分木を格納した配列
const int INF = INT_MAX;
rmq(int sz){} //やった。
// [l,r)が欲しいminの区間。
// [a,b)は現在見ている頂点の持つminの区間。最初は全区間。
// kはその頂点が格納されているindex
// b=nとしてデフォルト引数にメンバ変数nを使いたかったが、
// できないのでオーバーロード
int find(int l, int r){
return find(l,r,0,n,0);
}
int find(int l, int r, int a, int b, int k){
if(b<=l || r<=a) return INF; //区間外
else if(l<=a && b<=r) return segtree[k]; //区間内
//それ以外なら、子を調べてその結果をminしたものを返す。
else return
min(find(l,r,a,(a+b)/2,2*k+1),
find(l,r,(a+b)/2,b,2*k+2));
}
void update(int i, int x){} //後で
};
struct rmq{
int n; //size
vector<int> segtree; //完全二分木を格納した配列
const int INF = INT_MAX;
rmq(int sz){} //やった。
int find(int l, int r){} //やった
void update(int i, int x){
segtree[i+=n-1] = x; //元の配列のindexは頂点数2*n-1より、n-1足したもの。
while(i>0){
i = (i-1)/2; //親に移動
//子同士を比較して小さい方を親の値として更新。
segtree[i] = min(segtree[2*i+1],segtree[2*i+2]);
}
}
};
struct rmq{
int n; //size
vector<int> segtree; //完全二分木を格納した配列
const int INF = INT_MAX;
rmq(int sz){} //やった。
int find(int l, int r){} //やった
void update(int i, int x){} //やった
};
int main(){
rmq tr(10);
tr.update(5,3); //5番目の要素を3に更新する。
cout << tr.find(0,3) << endl; //INFが出力される
cout << tr.find(4,6) << endl; //3が出力される
}
群には逆元があるので、計算で求められる場所を省略できる。
群には逆元があるので、計算で求められる場所を省略できる。
群には逆元があるので、計算で求められる場所を省略できる。
By procon2019
発表日時 2019年10月4日(金) 18:30-21:00