Slide 1

Slide 1 text

ツリー上のマージテク亜種の話 camypaper

Slide 2

Slide 2 text

Q:ツリー上のマージテク亜種? A: CF383 Div.1 D の想定解で紹介されたやつ - (本家ではGuni(英語だとsackの意味)で呼ばれる) - (日本語では)名前はまだない? 詳しくは以下を見た方がよい http://codeforces.com/blog/entry/44351 僕の理解をもとにかいつまんで紹介 - 正確には彼らが紹介したい方法とはちょっと違います(上のURLでの3番)

Slide 3

Slide 3 text

問題 • N 頂点の K 種類の色で頂点彩色された 根付き木が与えられる • 頂点 i は色 Ci で塗られている • 以下の Q 個のクエリを処理せよ – 頂点 ai を根とする部分木に含まれる 色 bi で塗られた頂点の数を求めよ • N, Q, K ≦ 105 ぐらい

Slide 4

Slide 4 text

解法 • mapに(色, 個数)を持ってdfsしながら マージをがんばる – mapどうしはマージテクでマージ • O(N log2N) でできる – unordered_map を使うとlogが一個落ちる – でも重い • 今回は O(N logN) で軽めの定数倍で やります、という話

Slide 5

Slide 5 text

用意する関数 • dfs1(v) – v を根とする根付き木について処理 – 結果として 「v を根とする部分木に含まれる 各色の頂点がいくつあるか」を返す • dfs2(v) – v を根とする根付き木について処理 – 結果は返さない

Slide 6

Slide 6 text

dfs1(v) • v の直接の子孫を u1 , u2 , u3 , … , uk とする – 部分木のサイズの降順で並んでいる,とする • u2 , u3 , u4 , … , uk についてdfs2で処理 • u1 についてdfs1で処理して結果を受け取る – これを A とする • u2 , u3 , u4 , … , uk を根とする部分木に 含まれる頂点の色を全て調べて A に加算 • v 自身の色を A に加算 – このタイミングでクエリについて処理 • A を返す

Slide 7

Slide 7 text

dfs2(v) • v の直接の子孫を u1 , u2 , u3 , … , uk とする – 部分木のサイズの降順で並んでいる,とする • u2 , u3 , u4 , … , uk についてdfs2で処理 • u1 についてdfs1で処理して結果を受け取る – これを A とする • u2 , u3 , u4 , … , uk を根とする部分木に 含まれる頂点の色を全て調べて A に加算 • v 自身の色を A に加算 – このタイミングでクエリについて処理 • vを根とする部分木に含まれる頂点の色を 全て調べて A から減算

Slide 8

Slide 8 text

Q: 疑問 Q1: A を作る度に O(K) じゃん Q2: dfs2 で A から減算するパートが 明らかに不要に見えるんだけど??? A: A は 1 回だけ作って使いまわして解決 (そのためにdfs2で A から減算する) (さっきは分かりやすさのために説明を省きました)

Slide 9

Slide 9 text

アルゴリズム(簡易まとめ) • はじめに A を 1 個作る(使いまわす) • 根からdfsをする(dfs1でもdfs2でもよい) – 子孫たちのうち,サイズが最大のもの以外をdfs2で処理 • A は全て 0 のまま – サイズが最大のものをdfs1で処理 • A にデータが入る – 子孫たちのうち,最大のもの以外を全て追加 – 自身を追加 • ここで A は部分木全体について処理された状態になる – クエリに答える – (dfs2なら)子孫たちのうち,最大のもの以外を全て削除 • 削除されると A は全て 0 になる

Slide 10

Slide 10 text

• はじめに A を 1 個作る(使いまわす) • 根からdfsをする(dfs1でもdfs2でもよい) – 子孫たちのうち,サイズが最大のもの以外をdfs2で処理 • A は全て 0 のまま – サイズが最大のものをdfs1で処理 • A にデータが入る – 子孫たちのうち,最大のもの以外を全て追加 – 自身を追加 • ここで A は部分木全体について処理された状態になる – クエリに答える – (dfs2なら)子孫たちのうち,最大のもの以外を全て削除 • 削除されると A は全て 0 になる アルゴリズム(簡易まとめ) この2つはさらに 別のdfs等で処理

Slide 11

Slide 11 text

Slide 12

Slide 12 text

分かりやすさのため辺を2種類に分ける (実線はdfs1が,破線はdfs2が呼ばれる)

Slide 13

Slide 13 text

現在 dfs1(2) の開始時点とする A の状態 色 赤 緑 青 個数 0 0 0

Slide 14

Slide 14 text

dfs2(5) で頂点 5 の部分木を処理 A の状態 色 赤 緑 青 個数 0 0 0

Slide 15

Slide 15 text

dfs2(7) で頂点 7 の部分木を処理 A の状態 色 赤 緑 青 個数 0 0 0

Slide 16

Slide 16 text

dfs1(4) で頂点 4 の部分木を処理 A の状態 色 赤 緑 青 個数 0 0 0

Slide 17

Slide 17 text

dfs1(2) に戻ってきた A の状態 色 赤 緑 青 個数 1 1 1

Slide 18

Slide 18 text

頂点 5 からdfsして,頂点 5 の子孫たちを A に加算 A の状態 色 赤 緑 青 個数 1 2 2

Slide 19

Slide 19 text

頂点 7 からdfsして,頂点 7 の子孫たちを A に加算 A の状態 色 赤 緑 青 個数 1 3 2

Slide 20

Slide 20 text

頂点 2 自身を A に加算 A の状態 色 赤 緑 青 個数 1 3 3

Slide 21

Slide 21 text

ここでクエリに答える A の状態 色 赤 緑 青 個数 1 3 3

Slide 22

Slide 22 text

dfs2だった場合は最後に頂点 2 からdfsして 頂点 2 の子孫たちを A から減算 A の状態 色 赤 緑 青 個数 0 0 0

Slide 23

Slide 23 text

計算量解析 • 各頂点についてdfs1 か dfs2 が 1 回行われる • dfs1, dfs2 で破線の先の部分木をなめる操作, dfs2 での最後に部分木を全てなめる操作の 2 つがボトルネック • ある頂点について,なめられる回数は O(logN) – 根にたどり着くまでに辿る破線の数の 2 倍回ぐらい なめられる – 破線はHL分解でのlight edgeなので O(logN) 回で済む • N 個の頂点があるから O(NlogN)

Slide 24

Slide 24 text

まとめ • 各頂点を根とする部分木において 各色が含まれる数を求める,というのが 時間 O(NlogN), 空間 O(N + K) でできた • 利点と欠点 – unordered_mapを使うのに比べて定数倍が軽い – 実装はただマージテクするより少し重い

Slide 25

Slide 25 text

最後に • これマージテクマジ? – マージテクとして考えない方が(僕は) 分かりやすかった • 何かいい名前があれば,それで呼びたい