Upgrade to Pro — share decks privately, control downloads, hide ads and more …

区間と平方分割と私

Avatar for Kashee337 Kashee337
June 27, 2023
32

 区間と平方分割と私

社内勉強会資料

Avatar for Kashee337

Kashee337

June 27, 2023
Tweet

Transcript

  1. RMQを解く N個の要素を持つ数列に対して以下の2種類のクエリが 個与えられ処理することを考える。 find(l,r):区間 における要素の最小値を出力する update(i,v): 番目の要素を へ更新する 愚直に解くと、最悪ケースで かかってしまう。SegmentTreeで

    で解け る問題として典型的な問題。今回は平方分割で で解く。 ※ ここでは一点更新型のRMQを前提としている。補助データ構造を使えば区間更新へも対応 できるが今回は割愛。 Q [l, r) i v O(NQ) O(QlogN) O(Q ​ ) n 3
  2. 実装(1/3) バケットの初期化 で量子化したバケット単位で最小値を計算しておく。 vector<int> a; int n, b; vector<int> bucket;

    SQDC(vector<int>& _a) : a(_a) { n = _a.size(); b = sqrt(n); bucket.assign(int(n / b), MAX); for (int i = 0; i < n; i++) { bucket[i / b] = min(a[i], bucket[i / b]); } } ​ n 5
  3. 実装(2/3) find int find(int l, int r) { int tl

    = max(0, l); int tr = min(n - 1, r); int ans = MAX; // 左から探す while (tl < tr && tl % b != 0) ans = min(a[tl++], ans); // 右から探す while (tl < tr && (tr + 1) % b != 0) ans = min(a[tr--], ans); // bucket 単位で探す while (tl < tr) { int bi = tl / b; ans = min(ans, bucket[bi]); tl += b; } return ans; } 6
  4. 実装(3/3) update void update(int j, int v) { if (j

    < 0 || j >= n) return; int bj = j / b; a[j] = v; // bucket の最小値を更新 bucket[bj] = MAX; for (int i = b * bj; i < b * (bj + 1); i++) { bucket[i / b] = min(a[i], bucket[i / b]); } } 7
  5. 蟻本に乗ってる問題 POJ2104 K-th Number 整数値の数列 が与えられる。m個のクエリが与えられ、各クエリ に対して、部分列 を昇順にソートした時の 番目の要素を出 力せよ。

    制約 , SegmentTreeでも解けるが平方分割の方が実装がシンプルになる。 a ​ , a ​ , ..., a ​ 1 2 n Q(i, j, k) a ​ , a ​ , ..., a ​ i i+1 j k 1 ≤ n ≤ 100000 1 ≤ m ≤ 5000 8
  6. Mo's Algorythm 俗称クエリ平方分割と呼ばれるアルゴリズム。 区間に対するクエリをオフラインでまとめてバケット単位で処理することで高速化。 以下の条件を満たせば適用できる可能性がある。 要素の更新無し オフライン処理 区間 から の結果が高速に計算できる

    ※ 細かい説明を書こうかとも思ったがこのブログが分かり易すぎるので貼って見るスタイルで (https://ei1333.hateblo.jp/entry/2017/09/11/211011) [l, r) [l + 1, r), [l − 1, r), [l, r + 1), [l, r − 1) 11