SlideShare a Scribd company logo
1 of 30
Download to read offline
2012/3/20 NTTデータ駒場研修所 (情報オリンピック春合宿)




   プログラミングコンテストでの
             データ構造2
                 ~動的木編~

            東京大学情報理工学系研究科

                   秋葉 拓哉
                                      1
動的木
• 実は動的木にもいくつかある
 – 「順位キュー」的な言葉       (⇔ 二分ヒープ, フィボナッチヒープ)




• ポピュラーな動的木
 – Link-Cut 木
 – Euler-Tour 木 (動的グラフで内部的に使う)


• 今日は Link-Cut 木 の話をします

                                            2
Link-Cut 木
• 木を処理する神がかり的なデータ構造!!
 (木で木を処理するのでややこしい…)


• 以下が 𝑂(log 𝑛) 時間でできる.
 – 頂点の親を変更 (link) / 削除 (cut)
 – 木の根を求める (root) / 木の根を変更 (evert)
 – パスに対する頂点・枝の値のクエリ
  (sum・max・更新など)

                                     3
Link-Cut 木は現実
                   そんなのどうせ実装できっこない…
  (´・_・`)


                    IOI は Link-Cut 木で常勝!!
 Fan Haoqiang 氏
                               ※発言はフィクションです
      (中国)


Fan Haoqiang 氏は IOI’11 の Elephants で Link-Cut 木を実装!
• これに関して特別賞を受賞
• 599 点で準優勝

                                                      4
…でもやっぱその前に!
• 動的木は本当に必要?いつものじゃダメ?


• やはり,実装が面倒,避けられたら避けたい
• 実際のとこ,本当に必要になる問題は皆無


(特別賞を狙いたいのであればこの限りではない)




                          5
例
            木上の距離クエリ
    • 2 頂点間の枝を作成・削除してください
    • 2 頂点間の距離を答えてください
    ただしグラフは常に森.


           ツリーを処理するデータ構造なんて他に知らない…
               動的木じゃないとできっこないよ…
(´・_・`)

             平方分割でもできるよ (重要!!)
( ・`д・´)

                                     6
クエリを平方分割
クエリ          • クエリを 𝐵 個ごとのブロックに分割

      終わった
             • 各ブロック内で操作に関わる頂点は
       部分      高々2𝐵個
             • それ以外の頂点は興味が無いので,
      今やる
       部分      ブロックを処理する最初に,縮約
              – 𝑂(𝐵) 頂点の森になる




                                  7
クエリを平方分割
      クエリ            縮約
                                        • すると各クエリは 𝑂(𝐵) で処理可
                   ←
        𝑂(𝐵)         𝑂(𝑁)                      – 普通に 𝑂(𝐵) 頂点の木を探索するだけ
           …




                        縮約
                   ←                             𝑄
                        𝑂(𝑁)
        𝑂(𝐵)                              𝑂           𝑁 + 𝑄𝐵 = 𝑂 𝑄                                𝑁 時間
                                                 𝐵
           …




                                                                                            ↑
                        縮約                                                        𝐵=        𝑁 とした
                   ←
        𝑂(𝐵)            𝑂(𝑁)
           …




                                        ただし,クエリが先読みできないと無理


類似した問題と,より詳しい解説:
http://acm-icpc.aitea.net/index.php?plugin=attach&refer=2010%2FPractice%2F%B2%C6%B9%E7%BD%C9%2F%B9%D6%C9%BE&openfile=2d.pdf

                                                                                                                              8
レベル別 Link-Cut 木
オプション
          link, cut,     evert,   辺質問・更新
          頂点質問           頂点更新     (実装そこそこ)
         (実装一瞬)          (実装少し)


ベース                     expose
                       (実装そこそこ)


• オプションは独立に選んで実装可

• 「頂点質問」 = ある頂点から根までのパスにおける頂点の属
  性の sum とか max とかのこと
• 「頂点更新」 =とはパス上の頂点全部に x 足すとかのこと

                                             9
基本アイディア
• ツリーをパスの集合みたいに表現
• パスを平衡二分探索木で管理




                    10
基本アイディア
パスへの分解は決まってない + 固定じゃない




 こうなってるかもしれないし   こうなってるかも
                            11
核となる操作 expose(𝒗)
    頂点 v から根へのパスを繋げる



                                ←切れる

v                           v
                    切
                    れ



                        ↑
                    る




    平衡二分探索木の split / merge を使う
                                       12
link, cut の雰囲気
• cut(𝑣): 𝑣 から親への辺を削除
• link(𝑣, 𝑤): 𝑣 の親を 𝑤 にする
      平衡二分探索木を切ったり繋げたりするだけ



          w                 w
                    cut
              v                 v
                    link




                                    13
頂点クエリ
• sumv(𝑣): 頂点 v から根までの頂点たちに書いて
  ある数の和
 (あるいは minv, maxv, …)


やり方
• 平衡二分探索木に,部分木の和を持たせる
• expose(𝑣) して,和を見るだけ


                             14
各操作の計算量
結論: スプレー木を用いるとならし 𝑂 log 𝑛 時間

𝑂 log 2 𝑛 時間の略証:
• expose の計算量だけ考えれば良い (他は余裕)
• 平衡二分探索木で管理されるパスに入る・出る枝の本数
  をならし 𝑂 log 𝑛 本に抑えられれば良い


                        1 本 “入って”
     v             v    2 本 “出た”


                                    15
• 出るためには入る必要がある,入る回数を抑えれば OK
• 元の木の Heavy-Light Decomposition を考える
• Light-Edge の本数を抑える
  – 各頂点から根までの Light Edge はそもそも 𝑂 log 𝑛 本
  – よって,入る Light-Edgeは 𝑂 log 𝑛 本
• Heavy-Edge の本数を抑える
  – 1 度にいじる Heavy-Edge の本数は多いかも,でも,
  – Heavy-Edge が入るということは Light-Edge が出る
  – Light-Edge が出るためには Light-Edge が入ってるはず
  – それはさっき数えた! 各クエリ 𝑂 log 𝑛 本!
  – よってならし 𝑂 log 𝑛 本になる
• よって 𝑂 log 𝑛 ならし本.よって 𝑂 log 2 𝑛 ならし時間.

                                            16
各操作の計算量
𝑂 log 𝑛 時間:
• スプレー木のポテンシャルに踏み入って解析する
• 今日は省略


     Link-Cut 木を実装していこう!
              (気合い!)
     (といってもスプレー木が出来れば殆ど終わり)




                              17
実装:ノードの構造体
struct node_t {
 node_t *pp, *lp, *rp; // 親,左の子,右の子

// このノードは木の根?
bool is_root() {
  return !pp || (pp->lp != this && pp->rp != this);
}
…


Is_root は何故 !pp だけじゃない?
→ pp をパスの親を表すのにも使うため (後述)
 (こうすると実装が凄い楽になります)
親でありながら子でないことがある

                                                      18
実装:ノードの構造体 (続き)
void rotr() { // 右回転                                   void splay() { // スプレー操作
  node_t *q = pp, *r = q->pp;                            while (!is_root()) {
  if ((q->lp = rp)) rp->pp = q;                            node_t *q = pp;
  rp = q; q->pp = this;                                    if (q->is_root()) {
  if ((pp = r)) {                                            if (q->lp == this) rotr();
    if (r->lp == q) r->lp = this;                            else rotl();
    if (r->rp == q) r->rp = this;                          } else {
  }                                                          node_t *r = q->pp;
}                                                            if (r->lp == q) {
                                                               if (q->lp == this) { q->rotr(); rotr(); }
void rotl() { // 左回転                                           else { rotl(); rotr(); }
  node_t *q = pp, *r = q->pp;                                } else {
  if ((q->rp = lp)) lp->pp = q;                                if (q->rp == this) { q->rotl(); rotl(); }
  lp = q; q->pp = this;                                        else { rotr(); rotl(); }
  if ((pp = r)) {                                            }
    if (r->lp == q) r->lp = this;                          }
    if (r->rp == q) r->rp = this;                        }
  }                                                    }
}



               スプレー操作を Bottom-Up の方式で実装する
   (普通に平衡二分探索木でスプレー木を使うときは Top-Down の方式の方が良いが,
        今回はノードのポインタを知ってるところから splay したいので Bottom-Up)
           (参考:Top-Down の実装例 http://www.prefield.com/algorithm/container/splay_tree.html)

                                                                                                           19
実装:パスの親                    NULL

                                        3
    1
                                   2              5
        2               表現の例
                               1              4
    a       3

    b           4                       a
                    5                             c
    c
                                              b


• pp (親へのポインタ) を 2 通りの使い方をすると楽
 – 普通に二分探索木内での親へのポインタ(緑・青の両向き)
 – パスからの親へのポインタ (赤色の片方向)


                                                      20
expose(c)          1
    1
        2                                           2

            3                                 a         3
a
b               4                             b                 4
c                   5                                               5
                                              c

    NULL                                     NULL

        2                                     2
1           3                            1                  3
                        5                                           5
                4                                               4

            a                                       a
                    c                                           c
                b                                       b
                                         ノード 2 の右の子を
こうなってたら
                                        3 から a にするだけ!
                                                                        21
実装:expose
やること:                                       node_t *expose(node_t *x) {
                                              node_t *rp = NULL;
1. 今いる頂点を splay                               for (node_t *p = x; p; p = p->pp) {
                                                p->splay();
2. 右側にさっきまで居た木を                                 p->rp = rp;
                                                rp = p;
   くっつける                                      }
                                              x->splay(); // しとくと便利
3. 上の木に進む                                     return x;
                                            }



        3                   3               2                            2
                                                     3
    2           5       2           5   1                           1                     c
                                                             5
1           4       1           4                        4                    a
                                                                                      b
        a                           c                        c
                c           a                    a                                3
                                                                                          5
            b                   b                        b                            4

    さいしょ                Splay(c)        2 に移動,splay(2)             2 の右に c をつける
                                                                              22
実装:link, cut
expose まで出来ていればもう簡単

void cut(node_t *c) {
  expose(c);                        cut                         p
  node_t *p = c->lp;                •   c を expose                         c
  c->lp = NULL;
  p->pp = NULL;                     •   c の左を切断
}




                                                                        link
                                                          cut
void link(node_t *c, node_t *p) {
  expose(c);                        link
  expose(p);                        •     c, p を expose             p
  c->pp = p;                        •     p の右に c を                       c
  p->rp = c;                              つける
}

                                                                               23
実装:evert
evert(𝑣):𝑣 を根にする

木のパスの向きを反転すればよい

                   v
                       void evert(node_t *p) {
                         node_t *r = expose(p);
                         r->rev = true;
    v                  }

                            +
                         スプレー木に
                        反転の機能を実装

                                              24
実装:辺属性
• 木の辺に情報をつける
• 回転とか張替えでポインタと一緒に情報を保つ (注意深く)
• 部分木に関する情報も保つ
                         NULL


                        2 3          3
     1 1
                     1 2             4 5
        2 2
      5              1   5       4
    a      3 3
  6                          a
    b        4   4               6
  7              5                       c
    c
                                 b 7


                                             25
応用:Elephants (IOI’11)
• 象が 𝑁 匹並んでます

• クエリが大量にくる
 – Update(𝑖, 𝑥)
 – i 匹目の象を場所 x に移動


• クエリの度に答える
 – 何台のカメラで全員写る?
 – カメラ:幅 𝐿




                             26
応用:Elephants (IOI’11)
• 愚直な解法:クエリ毎に計算する
 – 貪欲法の典型的問題
 – 一番左の象を覆う,を繰り返せば良い


• 動的木を使う解法:
 – 似たような感じのことを木で表現する
 – 移動は木のちょっとした更新になる
 – よってクエリに爆速で答えられる

                          27
応用:Elephants (IOI’11)



• 象の場所に ● を書く
• そこから L 先に ○ を書き,●から辺を張る
• ○からはすぐ次の●か○に辺を張る

• これはツリー!
• 一番左から辿って,●の個数が答え!

                            28
Link-Cut 木まとめ
• まずはもっと容易な道具を検討!
 – クエリの平方分割


• 実装しよう (下に行くほど大変)
 – expose, link, cut, root, 頂点の情報に関する質問
 – evert, 頂点の情報の更新
 – 辺の情報に関する質問・更新



                                          29
全体まとめと私見
話したこと
•   平衡二分探索木
    – 本当に必要か検討,必要な場所だけ作るセグメント木
    – Treapのアイディア,楽な実装法の議論,応用例
    – その他の平衡二分探索木の紹介
•   動的木
    – 本当に必要か検討,クエリの平方分割
    – Link-Cut Tree のアイディア,楽な実装法の議論,応用例
私見
•   これらは難易度が高く,想定解法としての出題頻度は低い
•   よって,習得の優先度は高くない
•   ただし,非常に強力な道具なので,武器にできれば得をするかも?


                                          30

More Related Content

What's hot

最小カットを使って「燃やす埋める問題」を解く
最小カットを使って「燃やす埋める問題」を解く最小カットを使って「燃やす埋める問題」を解く
最小カットを使って「燃やす埋める問題」を解くshindannin
 
ウェーブレット木の世界
ウェーブレット木の世界ウェーブレット木の世界
ウェーブレット木の世界Preferred Networks
 
プログラミングコンテストでの動的計画法
プログラミングコンテストでの動的計画法プログラミングコンテストでの動的計画法
プログラミングコンテストでの動的計画法Takuya Akiba
 
様々な全域木問題
様々な全域木問題様々な全域木問題
様々な全域木問題tmaehara
 
競技プログラミングにおけるコードの書き方とその利便性
競技プログラミングにおけるコードの書き方とその利便性競技プログラミングにおけるコードの書き方とその利便性
競技プログラミングにおけるコードの書き方とその利便性Hibiki Yamashiro
 
AtCoder Regular Contest 039 解説
AtCoder Regular Contest 039 解説AtCoder Regular Contest 039 解説
AtCoder Regular Contest 039 解説AtCoder Inc.
 
Rolling Hashを殺す話
Rolling Hashを殺す話Rolling Hashを殺す話
Rolling Hashを殺す話Nagisa Eto
 
Re永続データ構造が分からない人のためのスライド
Re永続データ構造が分からない人のためのスライドRe永続データ構造が分からない人のためのスライド
Re永続データ構造が分からない人のためのスライドMasaki Hara
 
二部グラフの最小点被覆と最大安定集合と最小辺被覆の求め方
二部グラフの最小点被覆と最大安定集合と最小辺被覆の求め方二部グラフの最小点被覆と最大安定集合と最小辺被覆の求め方
二部グラフの最小点被覆と最大安定集合と最小辺被覆の求め方Kensuke Otsuki
 
Amortize analysis of Deque with 2 Stack
Amortize analysis of Deque with 2 StackAmortize analysis of Deque with 2 Stack
Amortize analysis of Deque with 2 StackKen Ogura
 
高速フーリエ変換
高速フーリエ変換高速フーリエ変換
高速フーリエ変換AtCoder Inc.
 
色々なダイクストラ高速化
色々なダイクストラ高速化色々なダイクストラ高速化
色々なダイクストラ高速化yosupo
 
区間分割の仕方を最適化する動的計画法 (JOI 2021 夏季セミナー)
区間分割の仕方を最適化する動的計画法 (JOI 2021 夏季セミナー)区間分割の仕方を最適化する動的計画法 (JOI 2021 夏季セミナー)
区間分割の仕方を最適化する動的計画法 (JOI 2021 夏季セミナー)Kensuke Otsuki
 
図と実装で理解する『木構造入門』
図と実装で理解する『木構造入門』図と実装で理解する『木構造入門』
図と実装で理解する『木構造入門』Proktmr
 

What's hot (20)

最小カットを使って「燃やす埋める問題」を解く
最小カットを使って「燃やす埋める問題」を解く最小カットを使って「燃やす埋める問題」を解く
最小カットを使って「燃やす埋める問題」を解く
 
ウェーブレット木の世界
ウェーブレット木の世界ウェーブレット木の世界
ウェーブレット木の世界
 
グラフネットワーク〜フロー&カット〜
グラフネットワーク〜フロー&カット〜グラフネットワーク〜フロー&カット〜
グラフネットワーク〜フロー&カット〜
 
プログラミングコンテストでの動的計画法
プログラミングコンテストでの動的計画法プログラミングコンテストでの動的計画法
プログラミングコンテストでの動的計画法
 
様々な全域木問題
様々な全域木問題様々な全域木問題
様々な全域木問題
 
Binary indexed tree
Binary indexed treeBinary indexed tree
Binary indexed tree
 
競技プログラミングにおけるコードの書き方とその利便性
競技プログラミングにおけるコードの書き方とその利便性競技プログラミングにおけるコードの書き方とその利便性
競技プログラミングにおけるコードの書き方とその利便性
 
Rolling hash
Rolling hashRolling hash
Rolling hash
 
全域木いろいろ
全域木いろいろ全域木いろいろ
全域木いろいろ
 
明日使えないすごいビット演算
明日使えないすごいビット演算明日使えないすごいビット演算
明日使えないすごいビット演算
 
AtCoder Regular Contest 039 解説
AtCoder Regular Contest 039 解説AtCoder Regular Contest 039 解説
AtCoder Regular Contest 039 解説
 
Rolling Hashを殺す話
Rolling Hashを殺す話Rolling Hashを殺す話
Rolling Hashを殺す話
 
Re永続データ構造が分からない人のためのスライド
Re永続データ構造が分からない人のためのスライドRe永続データ構造が分からない人のためのスライド
Re永続データ構造が分からない人のためのスライド
 
二部グラフの最小点被覆と最大安定集合と最小辺被覆の求め方
二部グラフの最小点被覆と最大安定集合と最小辺被覆の求め方二部グラフの最小点被覆と最大安定集合と最小辺被覆の求め方
二部グラフの最小点被覆と最大安定集合と最小辺被覆の求め方
 
写像 12 相
写像 12 相写像 12 相
写像 12 相
 
Amortize analysis of Deque with 2 Stack
Amortize analysis of Deque with 2 StackAmortize analysis of Deque with 2 Stack
Amortize analysis of Deque with 2 Stack
 
高速フーリエ変換
高速フーリエ変換高速フーリエ変換
高速フーリエ変換
 
色々なダイクストラ高速化
色々なダイクストラ高速化色々なダイクストラ高速化
色々なダイクストラ高速化
 
区間分割の仕方を最適化する動的計画法 (JOI 2021 夏季セミナー)
区間分割の仕方を最適化する動的計画法 (JOI 2021 夏季セミナー)区間分割の仕方を最適化する動的計画法 (JOI 2021 夏季セミナー)
区間分割の仕方を最適化する動的計画法 (JOI 2021 夏季セミナー)
 
図と実装で理解する『木構造入門』
図と実装で理解する『木構造入門』図と実装で理解する『木構造入門』
図と実装で理解する『木構造入門』
 

Viewers also liked

LCA and RMQ ~簡潔もあるよ!~
LCA and RMQ ~簡潔もあるよ!~LCA and RMQ ~簡潔もあるよ!~
LCA and RMQ ~簡潔もあるよ!~Yuma Inoue
 
CVPR2016読み会 Sparsifying Neural Network Connections for Face Recognition
CVPR2016読み会 Sparsifying Neural Network Connections for Face RecognitionCVPR2016読み会 Sparsifying Neural Network Connections for Face Recognition
CVPR2016読み会 Sparsifying Neural Network Connections for Face RecognitionKoichi Takahashi
 
20170819 CV勉強会 CVPR 2017
20170819 CV勉強会 CVPR 201720170819 CV勉強会 CVPR 2017
20170819 CV勉強会 CVPR 2017issaymk2
 
On the Dynamics of Machine Learning Algorithms and Behavioral Game Theory
On the Dynamics of Machine Learning Algorithms and Behavioral Game TheoryOn the Dynamics of Machine Learning Algorithms and Behavioral Game Theory
On the Dynamics of Machine Learning Algorithms and Behavioral Game TheoryRikiya Takahashi
 
Stochastic Variational Inference
Stochastic Variational InferenceStochastic Variational Inference
Stochastic Variational InferenceKaede Hayashi
 
Greed is Good: 劣モジュラ関数最大化とその発展
Greed is Good: 劣モジュラ関数最大化とその発展Greed is Good: 劣モジュラ関数最大化とその発展
Greed is Good: 劣モジュラ関数最大化とその発展Yuichi Yoshida
 
sublabel accurate convex relaxation of vectorial multilabel energies
sublabel accurate convex relaxation of vectorial multilabel energiessublabel accurate convex relaxation of vectorial multilabel energies
sublabel accurate convex relaxation of vectorial multilabel energiesFujimoto Keisuke
 
PRML輪読#14
PRML輪読#14PRML輪読#14
PRML輪読#14matsuolab
 
Fractality of Massive Graphs: Scalable Analysis with Sketch-Based Box-Coverin...
Fractality of Massive Graphs: Scalable Analysis with Sketch-Based Box-Coverin...Fractality of Massive Graphs: Scalable Analysis with Sketch-Based Box-Coverin...
Fractality of Massive Graphs: Scalable Analysis with Sketch-Based Box-Coverin...Kenko Nakamura
 
Practical recommendations for gradient-based training of deep architectures
Practical recommendations for gradient-based training of deep architecturesPractical recommendations for gradient-based training of deep architectures
Practical recommendations for gradient-based training of deep architecturesKoji Matsuda
 
ORB-SLAMを動かしてみた
ORB-SLAMを動かしてみたORB-SLAMを動かしてみた
ORB-SLAMを動かしてみたTakuya Minagawa
 
強化学習その2
強化学習その2強化学習その2
強化学習その2nishio
 
多項式あてはめで眺めるベイズ推定 ~今日からきみもベイジアン~
多項式あてはめで眺めるベイズ推定~今日からきみもベイジアン~多項式あてはめで眺めるベイズ推定~今日からきみもベイジアン~
多項式あてはめで眺めるベイズ推定 ~今日からきみもベイジアン~ tanutarou
 
LiDAR点群とSfM点群との位置合わせ
LiDAR点群とSfM点群との位置合わせLiDAR点群とSfM点群との位置合わせ
LiDAR点群とSfM点群との位置合わせTakuya Minagawa
 
画像認識モデルを作るための鉄板レシピ
画像認識モデルを作るための鉄板レシピ画像認識モデルを作るための鉄板レシピ
画像認識モデルを作るための鉄板レシピTakahiro Kubo
 

Viewers also liked (16)

LCA and RMQ ~簡潔もあるよ!~
LCA and RMQ ~簡潔もあるよ!~LCA and RMQ ~簡潔もあるよ!~
LCA and RMQ ~簡潔もあるよ!~
 
CVPR2016読み会 Sparsifying Neural Network Connections for Face Recognition
CVPR2016読み会 Sparsifying Neural Network Connections for Face RecognitionCVPR2016読み会 Sparsifying Neural Network Connections for Face Recognition
CVPR2016読み会 Sparsifying Neural Network Connections for Face Recognition
 
20170819 CV勉強会 CVPR 2017
20170819 CV勉強会 CVPR 201720170819 CV勉強会 CVPR 2017
20170819 CV勉強会 CVPR 2017
 
On the Dynamics of Machine Learning Algorithms and Behavioral Game Theory
On the Dynamics of Machine Learning Algorithms and Behavioral Game TheoryOn the Dynamics of Machine Learning Algorithms and Behavioral Game Theory
On the Dynamics of Machine Learning Algorithms and Behavioral Game Theory
 
Stochastic Variational Inference
Stochastic Variational InferenceStochastic Variational Inference
Stochastic Variational Inference
 
DeepLearningTutorial
DeepLearningTutorialDeepLearningTutorial
DeepLearningTutorial
 
Greed is Good: 劣モジュラ関数最大化とその発展
Greed is Good: 劣モジュラ関数最大化とその発展Greed is Good: 劣モジュラ関数最大化とその発展
Greed is Good: 劣モジュラ関数最大化とその発展
 
sublabel accurate convex relaxation of vectorial multilabel energies
sublabel accurate convex relaxation of vectorial multilabel energiessublabel accurate convex relaxation of vectorial multilabel energies
sublabel accurate convex relaxation of vectorial multilabel energies
 
PRML輪読#14
PRML輪読#14PRML輪読#14
PRML輪読#14
 
Fractality of Massive Graphs: Scalable Analysis with Sketch-Based Box-Coverin...
Fractality of Massive Graphs: Scalable Analysis with Sketch-Based Box-Coverin...Fractality of Massive Graphs: Scalable Analysis with Sketch-Based Box-Coverin...
Fractality of Massive Graphs: Scalable Analysis with Sketch-Based Box-Coverin...
 
Practical recommendations for gradient-based training of deep architectures
Practical recommendations for gradient-based training of deep architecturesPractical recommendations for gradient-based training of deep architectures
Practical recommendations for gradient-based training of deep architectures
 
ORB-SLAMを動かしてみた
ORB-SLAMを動かしてみたORB-SLAMを動かしてみた
ORB-SLAMを動かしてみた
 
強化学習その2
強化学習その2強化学習その2
強化学習その2
 
多項式あてはめで眺めるベイズ推定 ~今日からきみもベイジアン~
多項式あてはめで眺めるベイズ推定~今日からきみもベイジアン~多項式あてはめで眺めるベイズ推定~今日からきみもベイジアン~
多項式あてはめで眺めるベイズ推定 ~今日からきみもベイジアン~
 
LiDAR点群とSfM点群との位置合わせ
LiDAR点群とSfM点群との位置合わせLiDAR点群とSfM点群との位置合わせ
LiDAR点群とSfM点群との位置合わせ
 
画像認識モデルを作るための鉄板レシピ
画像認識モデルを作るための鉄板レシピ画像認識モデルを作るための鉄板レシピ
画像認識モデルを作るための鉄板レシピ
 

Similar to プログラミングコンテストでのデータ構造 2 ~動的木編~

純粋関数型アルゴリズム入門
純粋関数型アルゴリズム入門純粋関数型アルゴリズム入門
純粋関数型アルゴリズム入門Kimikazu Kato
 
PFDS 5.5 Pairing heap
PFDS 5.5 Pairing heapPFDS 5.5 Pairing heap
PFDS 5.5 Pairing heap昌平 村山
 
Real World OCamlを読んでLispと協調してみた
Real World OCamlを読んでLispと協調してみたReal World OCamlを読んでLispと協調してみた
Real World OCamlを読んでLispと協調してみたblackenedgold
 
数式をnumpyに落としこむコツ
数式をnumpyに落としこむコツ数式をnumpyに落としこむコツ
数式をnumpyに落としこむコツShuyo Nakatani
 
Lisp tutorial for Pythonista : Day 1
Lisp tutorial for Pythonista : Day 1Lisp tutorial for Pythonista : Day 1
Lisp tutorial for Pythonista : Day 1Ransui Iso
 
USP友の会勉強会、状態遷移図の巻
USP友の会勉強会、状態遷移図の巻USP友の会勉強会、状態遷移図の巻
USP友の会勉強会、状態遷移図の巻Ryuichi Ueda
 
Lisp tutorial for Pythonista : Day 2
Lisp tutorial for Pythonista : Day 2Lisp tutorial for Pythonista : Day 2
Lisp tutorial for Pythonista : Day 2Ransui Iso
 
R による文書分類入門
R による文書分類入門R による文書分類入門
R による文書分類入門Takeshi Arabiki
 
IbisPaintのOpenGLES2.0
IbisPaintのOpenGLES2.0IbisPaintのOpenGLES2.0
IbisPaintのOpenGLES2.0Eiji Kamiya
 
Lisp Tutorial for Pythonista : Day 3
Lisp Tutorial for Pythonista : Day 3Lisp Tutorial for Pythonista : Day 3
Lisp Tutorial for Pythonista : Day 3Ransui Iso
 
関数プログラミング入門
関数プログラミング入門関数プログラミング入門
関数プログラミング入門Hideyuki Tanaka
 
Lisp Tutorial for Pythonista : Day 5
Lisp Tutorial for Pythonista : Day 5Lisp Tutorial for Pythonista : Day 5
Lisp Tutorial for Pythonista : Day 5Ransui Iso
 
transformer解説~Chat-GPTの源流~
transformer解説~Chat-GPTの源流~transformer解説~Chat-GPTの源流~
transformer解説~Chat-GPTの源流~MasayoshiTsutsui
 
Material
MaterialMaterial
Material_TUNE_
 
Introduction to NumPy & SciPy
Introduction to NumPy & SciPyIntroduction to NumPy & SciPy
Introduction to NumPy & SciPyShiqiao Du
 
PRML復々習レーン#9 6.3-6.3.1
PRML復々習レーン#9 6.3-6.3.1PRML復々習レーン#9 6.3-6.3.1
PRML復々習レーン#9 6.3-6.3.1sleepy_yoshi
 

Similar to プログラミングコンテストでのデータ構造 2 ~動的木編~ (20)

純粋関数型アルゴリズム入門
純粋関数型アルゴリズム入門純粋関数型アルゴリズム入門
純粋関数型アルゴリズム入門
 
PFDS 5.5 Pairing heap
PFDS 5.5 Pairing heapPFDS 5.5 Pairing heap
PFDS 5.5 Pairing heap
 
Real World OCamlを読んでLispと協調してみた
Real World OCamlを読んでLispと協調してみたReal World OCamlを読んでLispと協調してみた
Real World OCamlを読んでLispと協調してみた
 
数式をnumpyに落としこむコツ
数式をnumpyに落としこむコツ数式をnumpyに落としこむコツ
数式をnumpyに落としこむコツ
 
Lisp tutorial for Pythonista : Day 1
Lisp tutorial for Pythonista : Day 1Lisp tutorial for Pythonista : Day 1
Lisp tutorial for Pythonista : Day 1
 
USP友の会勉強会、状態遷移図の巻
USP友の会勉強会、状態遷移図の巻USP友の会勉強会、状態遷移図の巻
USP友の会勉強会、状態遷移図の巻
 
Lisp tutorial for Pythonista : Day 2
Lisp tutorial for Pythonista : Day 2Lisp tutorial for Pythonista : Day 2
Lisp tutorial for Pythonista : Day 2
 
直交領域探索
直交領域探索直交領域探索
直交領域探索
 
T77 episteme
T77 epistemeT77 episteme
T77 episteme
 
R による文書分類入門
R による文書分類入門R による文書分類入門
R による文書分類入門
 
IbisPaintのOpenGLES2.0
IbisPaintのOpenGLES2.0IbisPaintのOpenGLES2.0
IbisPaintのOpenGLES2.0
 
Lisp Tutorial for Pythonista : Day 3
Lisp Tutorial for Pythonista : Day 3Lisp Tutorial for Pythonista : Day 3
Lisp Tutorial for Pythonista : Day 3
 
PFI Christmas seminar 2009
PFI Christmas seminar 2009PFI Christmas seminar 2009
PFI Christmas seminar 2009
 
関数プログラミング入門
関数プログラミング入門関数プログラミング入門
関数プログラミング入門
 
Lisp Tutorial for Pythonista : Day 5
Lisp Tutorial for Pythonista : Day 5Lisp Tutorial for Pythonista : Day 5
Lisp Tutorial for Pythonista : Day 5
 
transformer解説~Chat-GPTの源流~
transformer解説~Chat-GPTの源流~transformer解説~Chat-GPTの源流~
transformer解説~Chat-GPTの源流~
 
Material
MaterialMaterial
Material
 
Introduction to NumPy & SciPy
Introduction to NumPy & SciPyIntroduction to NumPy & SciPy
Introduction to NumPy & SciPy
 
PRML復々習レーン#9 6.3-6.3.1
PRML復々習レーン#9 6.3-6.3.1PRML復々習レーン#9 6.3-6.3.1
PRML復々習レーン#9 6.3-6.3.1
 
HPC Phys-20201203
HPC Phys-20201203HPC Phys-20201203
HPC Phys-20201203
 

More from Takuya Akiba

分散深層学習 @ NIPS'17
分散深層学習 @ NIPS'17分散深層学習 @ NIPS'17
分散深層学習 @ NIPS'17Takuya Akiba
 
Learning Convolutional Neural Networks for Graphs
Learning Convolutional Neural Networks for GraphsLearning Convolutional Neural Networks for Graphs
Learning Convolutional Neural Networks for GraphsTakuya Akiba
 
TCO15 Algorithm Round 2C 解説
TCO15 Algorithm Round 2C 解説TCO15 Algorithm Round 2C 解説
TCO15 Algorithm Round 2C 解説Takuya Akiba
 
ACM-ICPC 世界大会 2015 問題 K "Tours" 解説
ACM-ICPC 世界大会 2015 問題 K "Tours" 解説ACM-ICPC 世界大会 2015 問題 K "Tours" 解説
ACM-ICPC 世界大会 2015 問題 K "Tours" 解説Takuya Akiba
 
大規模グラフ解析のための乱択スケッチ技法
大規模グラフ解析のための乱択スケッチ技法大規模グラフ解析のための乱択スケッチ技法
大規模グラフ解析のための乱択スケッチ技法Takuya Akiba
 
乱択データ構造の最新事情 -MinHash と HyperLogLog の最近の進歩-
乱択データ構造の最新事情 -MinHash と HyperLogLog の最近の進歩-乱択データ構造の最新事情 -MinHash と HyperLogLog の最近の進歩-
乱択データ構造の最新事情 -MinHash と HyperLogLog の最近の進歩-Takuya Akiba
 
Cache-Oblivious データ構造入門 @DSIRNLP#5
Cache-Oblivious データ構造入門 @DSIRNLP#5Cache-Oblivious データ構造入門 @DSIRNLP#5
Cache-Oblivious データ構造入門 @DSIRNLP#5Takuya Akiba
 
平面グラフと交通ネットワークのアルゴリズム
平面グラフと交通ネットワークのアルゴリズム平面グラフと交通ネットワークのアルゴリズム
平面グラフと交通ネットワークのアルゴリズムTakuya Akiba
 
大規模ネットワークの性質と先端グラフアルゴリズム
大規模ネットワークの性質と先端グラフアルゴリズム大規模ネットワークの性質と先端グラフアルゴリズム
大規模ネットワークの性質と先端グラフアルゴリズムTakuya Akiba
 
勉強か?趣味か?人生か?―プログラミングコンテストとは
勉強か?趣味か?人生か?―プログラミングコンテストとは勉強か?趣味か?人生か?―プログラミングコンテストとは
勉強か?趣味か?人生か?―プログラミングコンテストとはTakuya Akiba
 
大規模グラフアルゴリズムの最先端
大規模グラフアルゴリズムの最先端大規模グラフアルゴリズムの最先端
大規模グラフアルゴリズムの最先端Takuya Akiba
 

More from Takuya Akiba (11)

分散深層学習 @ NIPS'17
分散深層学習 @ NIPS'17分散深層学習 @ NIPS'17
分散深層学習 @ NIPS'17
 
Learning Convolutional Neural Networks for Graphs
Learning Convolutional Neural Networks for GraphsLearning Convolutional Neural Networks for Graphs
Learning Convolutional Neural Networks for Graphs
 
TCO15 Algorithm Round 2C 解説
TCO15 Algorithm Round 2C 解説TCO15 Algorithm Round 2C 解説
TCO15 Algorithm Round 2C 解説
 
ACM-ICPC 世界大会 2015 問題 K "Tours" 解説
ACM-ICPC 世界大会 2015 問題 K "Tours" 解説ACM-ICPC 世界大会 2015 問題 K "Tours" 解説
ACM-ICPC 世界大会 2015 問題 K "Tours" 解説
 
大規模グラフ解析のための乱択スケッチ技法
大規模グラフ解析のための乱択スケッチ技法大規模グラフ解析のための乱択スケッチ技法
大規模グラフ解析のための乱択スケッチ技法
 
乱択データ構造の最新事情 -MinHash と HyperLogLog の最近の進歩-
乱択データ構造の最新事情 -MinHash と HyperLogLog の最近の進歩-乱択データ構造の最新事情 -MinHash と HyperLogLog の最近の進歩-
乱択データ構造の最新事情 -MinHash と HyperLogLog の最近の進歩-
 
Cache-Oblivious データ構造入門 @DSIRNLP#5
Cache-Oblivious データ構造入門 @DSIRNLP#5Cache-Oblivious データ構造入門 @DSIRNLP#5
Cache-Oblivious データ構造入門 @DSIRNLP#5
 
平面グラフと交通ネットワークのアルゴリズム
平面グラフと交通ネットワークのアルゴリズム平面グラフと交通ネットワークのアルゴリズム
平面グラフと交通ネットワークのアルゴリズム
 
大規模ネットワークの性質と先端グラフアルゴリズム
大規模ネットワークの性質と先端グラフアルゴリズム大規模ネットワークの性質と先端グラフアルゴリズム
大規模ネットワークの性質と先端グラフアルゴリズム
 
勉強か?趣味か?人生か?―プログラミングコンテストとは
勉強か?趣味か?人生か?―プログラミングコンテストとは勉強か?趣味か?人生か?―プログラミングコンテストとは
勉強か?趣味か?人生か?―プログラミングコンテストとは
 
大規模グラフアルゴリズムの最先端
大規模グラフアルゴリズムの最先端大規模グラフアルゴリズムの最先端
大規模グラフアルゴリズムの最先端
 

プログラミングコンテストでのデータ構造 2 ~動的木編~

  • 1. 2012/3/20 NTTデータ駒場研修所 (情報オリンピック春合宿) プログラミングコンテストでの データ構造2 ~動的木編~ 東京大学情報理工学系研究科 秋葉 拓哉 1
  • 2. 動的木 • 実は動的木にもいくつかある – 「順位キュー」的な言葉 (⇔ 二分ヒープ, フィボナッチヒープ) • ポピュラーな動的木 – Link-Cut 木 – Euler-Tour 木 (動的グラフで内部的に使う) • 今日は Link-Cut 木 の話をします 2
  • 3. Link-Cut 木 • 木を処理する神がかり的なデータ構造!! (木で木を処理するのでややこしい…) • 以下が 𝑂(log 𝑛) 時間でできる. – 頂点の親を変更 (link) / 削除 (cut) – 木の根を求める (root) / 木の根を変更 (evert) – パスに対する頂点・枝の値のクエリ (sum・max・更新など) 3
  • 4. Link-Cut 木は現実 そんなのどうせ実装できっこない… (´・_・`) IOI は Link-Cut 木で常勝!! Fan Haoqiang 氏 ※発言はフィクションです (中国) Fan Haoqiang 氏は IOI’11 の Elephants で Link-Cut 木を実装! • これに関して特別賞を受賞 • 599 点で準優勝 4
  • 5. …でもやっぱその前に! • 動的木は本当に必要?いつものじゃダメ? • やはり,実装が面倒,避けられたら避けたい • 実際のとこ,本当に必要になる問題は皆無 (特別賞を狙いたいのであればこの限りではない) 5
  • 6. 木上の距離クエリ • 2 頂点間の枝を作成・削除してください • 2 頂点間の距離を答えてください ただしグラフは常に森. ツリーを処理するデータ構造なんて他に知らない… 動的木じゃないとできっこないよ… (´・_・`) 平方分割でもできるよ (重要!!) ( ・`д・´) 6
  • 7. クエリを平方分割 クエリ • クエリを 𝐵 個ごとのブロックに分割 終わった • 各ブロック内で操作に関わる頂点は 部分 高々2𝐵個 • それ以外の頂点は興味が無いので, 今やる 部分 ブロックを処理する最初に,縮約 – 𝑂(𝐵) 頂点の森になる 7
  • 8. クエリを平方分割 クエリ 縮約 • すると各クエリは 𝑂(𝐵) で処理可 ← 𝑂(𝐵) 𝑂(𝑁) – 普通に 𝑂(𝐵) 頂点の木を探索するだけ … 縮約 ← 𝑄 𝑂(𝑁) 𝑂(𝐵) 𝑂 𝑁 + 𝑄𝐵 = 𝑂 𝑄 𝑁 時間 𝐵 … ↑ 縮約 𝐵= 𝑁 とした ← 𝑂(𝐵) 𝑂(𝑁) … ただし,クエリが先読みできないと無理 類似した問題と,より詳しい解説: http://acm-icpc.aitea.net/index.php?plugin=attach&refer=2010%2FPractice%2F%B2%C6%B9%E7%BD%C9%2F%B9%D6%C9%BE&openfile=2d.pdf 8
  • 9. レベル別 Link-Cut 木 オプション link, cut, evert, 辺質問・更新 頂点質問 頂点更新 (実装そこそこ) (実装一瞬) (実装少し) ベース expose (実装そこそこ) • オプションは独立に選んで実装可 • 「頂点質問」 = ある頂点から根までのパスにおける頂点の属 性の sum とか max とかのこと • 「頂点更新」 =とはパス上の頂点全部に x 足すとかのこと 9
  • 11. 基本アイディア パスへの分解は決まってない + 固定じゃない こうなってるかもしれないし こうなってるかも 11
  • 12. 核となる操作 expose(𝒗) 頂点 v から根へのパスを繋げる ←切れる v v 切 れ ↑ る 平衡二分探索木の split / merge を使う 12
  • 13. link, cut の雰囲気 • cut(𝑣): 𝑣 から親への辺を削除 • link(𝑣, 𝑤): 𝑣 の親を 𝑤 にする 平衡二分探索木を切ったり繋げたりするだけ w w cut v v link 13
  • 14. 頂点クエリ • sumv(𝑣): 頂点 v から根までの頂点たちに書いて ある数の和 (あるいは minv, maxv, …) やり方 • 平衡二分探索木に,部分木の和を持たせる • expose(𝑣) して,和を見るだけ 14
  • 15. 各操作の計算量 結論: スプレー木を用いるとならし 𝑂 log 𝑛 時間 𝑂 log 2 𝑛 時間の略証: • expose の計算量だけ考えれば良い (他は余裕) • 平衡二分探索木で管理されるパスに入る・出る枝の本数 をならし 𝑂 log 𝑛 本に抑えられれば良い 1 本 “入って” v v 2 本 “出た” 15
  • 16. • 出るためには入る必要がある,入る回数を抑えれば OK • 元の木の Heavy-Light Decomposition を考える • Light-Edge の本数を抑える – 各頂点から根までの Light Edge はそもそも 𝑂 log 𝑛 本 – よって,入る Light-Edgeは 𝑂 log 𝑛 本 • Heavy-Edge の本数を抑える – 1 度にいじる Heavy-Edge の本数は多いかも,でも, – Heavy-Edge が入るということは Light-Edge が出る – Light-Edge が出るためには Light-Edge が入ってるはず – それはさっき数えた! 各クエリ 𝑂 log 𝑛 本! – よってならし 𝑂 log 𝑛 本になる • よって 𝑂 log 𝑛 ならし本.よって 𝑂 log 2 𝑛 ならし時間. 16
  • 17. 各操作の計算量 𝑂 log 𝑛 時間: • スプレー木のポテンシャルに踏み入って解析する • 今日は省略 Link-Cut 木を実装していこう! (気合い!) (といってもスプレー木が出来れば殆ど終わり) 17
  • 18. 実装:ノードの構造体 struct node_t { node_t *pp, *lp, *rp; // 親,左の子,右の子 // このノードは木の根? bool is_root() { return !pp || (pp->lp != this && pp->rp != this); } … Is_root は何故 !pp だけじゃない? → pp をパスの親を表すのにも使うため (後述) (こうすると実装が凄い楽になります) 親でありながら子でないことがある 18
  • 19. 実装:ノードの構造体 (続き) void rotr() { // 右回転 void splay() { // スプレー操作 node_t *q = pp, *r = q->pp; while (!is_root()) { if ((q->lp = rp)) rp->pp = q; node_t *q = pp; rp = q; q->pp = this; if (q->is_root()) { if ((pp = r)) { if (q->lp == this) rotr(); if (r->lp == q) r->lp = this; else rotl(); if (r->rp == q) r->rp = this; } else { } node_t *r = q->pp; } if (r->lp == q) { if (q->lp == this) { q->rotr(); rotr(); } void rotl() { // 左回転 else { rotl(); rotr(); } node_t *q = pp, *r = q->pp; } else { if ((q->rp = lp)) lp->pp = q; if (q->rp == this) { q->rotl(); rotl(); } lp = q; q->pp = this; else { rotr(); rotl(); } if ((pp = r)) { } if (r->lp == q) r->lp = this; } if (r->rp == q) r->rp = this; } } } } スプレー操作を Bottom-Up の方式で実装する (普通に平衡二分探索木でスプレー木を使うときは Top-Down の方式の方が良いが, 今回はノードのポインタを知ってるところから splay したいので Bottom-Up) (参考:Top-Down の実装例 http://www.prefield.com/algorithm/container/splay_tree.html) 19
  • 20. 実装:パスの親 NULL 3 1 2 5 2 表現の例 1 4 a 3 b 4 a 5 c c b • pp (親へのポインタ) を 2 通りの使い方をすると楽 – 普通に二分探索木内での親へのポインタ(緑・青の両向き) – パスからの親へのポインタ (赤色の片方向) 20
  • 21. expose(c) 1 1 2 2 3 a 3 a b 4 b 4 c 5 5 c NULL NULL 2 2 1 3 1 3 5 5 4 4 a a c c b b ノード 2 の右の子を こうなってたら 3 から a にするだけ! 21
  • 22. 実装:expose やること: node_t *expose(node_t *x) { node_t *rp = NULL; 1. 今いる頂点を splay for (node_t *p = x; p; p = p->pp) { p->splay(); 2. 右側にさっきまで居た木を p->rp = rp; rp = p; くっつける } x->splay(); // しとくと便利 3. 上の木に進む return x; } 3 3 2 2 3 2 5 2 5 1 1 c 5 1 4 1 4 4 a b a c c c a a 3 5 b b b 4 さいしょ Splay(c) 2 に移動,splay(2) 2 の右に c をつける 22
  • 23. 実装:link, cut expose まで出来ていればもう簡単 void cut(node_t *c) { expose(c); cut p node_t *p = c->lp; • c を expose c c->lp = NULL; p->pp = NULL; • c の左を切断 } link cut void link(node_t *c, node_t *p) { expose(c); link expose(p); • c, p を expose p c->pp = p; • p の右に c を c p->rp = c; つける } 23
  • 24. 実装:evert evert(𝑣):𝑣 を根にする 木のパスの向きを反転すればよい v void evert(node_t *p) { node_t *r = expose(p); r->rev = true; v } + スプレー木に 反転の機能を実装 24
  • 25. 実装:辺属性 • 木の辺に情報をつける • 回転とか張替えでポインタと一緒に情報を保つ (注意深く) • 部分木に関する情報も保つ NULL 2 3 3 1 1 1 2 4 5 2 2 5 1 5 4 a 3 3 6 a b 4 4 6 7 5 c c b 7 25
  • 26. 応用:Elephants (IOI’11) • 象が 𝑁 匹並んでます • クエリが大量にくる – Update(𝑖, 𝑥) – i 匹目の象を場所 x に移動 • クエリの度に答える – 何台のカメラで全員写る? – カメラ:幅 𝐿 26
  • 27. 応用:Elephants (IOI’11) • 愚直な解法:クエリ毎に計算する – 貪欲法の典型的問題 – 一番左の象を覆う,を繰り返せば良い • 動的木を使う解法: – 似たような感じのことを木で表現する – 移動は木のちょっとした更新になる – よってクエリに爆速で答えられる 27
  • 28. 応用:Elephants (IOI’11) • 象の場所に ● を書く • そこから L 先に ○ を書き,●から辺を張る • ○からはすぐ次の●か○に辺を張る • これはツリー! • 一番左から辿って,●の個数が答え! 28
  • 29. Link-Cut 木まとめ • まずはもっと容易な道具を検討! – クエリの平方分割 • 実装しよう (下に行くほど大変) – expose, link, cut, root, 頂点の情報に関する質問 – evert, 頂点の情報の更新 – 辺の情報に関する質問・更新 29
  • 30. 全体まとめと私見 話したこと • 平衡二分探索木 – 本当に必要か検討,必要な場所だけ作るセグメント木 – Treapのアイディア,楽な実装法の議論,応用例 – その他の平衡二分探索木の紹介 • 動的木 – 本当に必要か検討,クエリの平方分割 – Link-Cut Tree のアイディア,楽な実装法の議論,応用例 私見 • これらは難易度が高く,想定解法としての出題頻度は低い • よって,習得の優先度は高くない • ただし,非常に強力な道具なので,武器にできれば得をするかも? 30