1 / 277




               Masaki Hara (qnighy)
2013年 情報オリンピック春期トレーニング合宿にて
2 / 277




 有向木がたくさんあります
 以下のクエリに高速で答えてください
1. Aの親をBにする
     ◦ Aは木の根で、BはAの子孫ではない
2.   Aを親から切り離して木の根にする
3.   A,Bが同じ木に属するか判定し、
     同じ木に属する場合はLCAを求める
3 / 277

                                   K

   有向木がたくさんあります
                                   D


                       B       E       I

               F           A       H       C


       J   L       G
4 / 277

                                   K

   有向木がたくさんあります
   以下のクエリに高速で答えてください              D


                       B       E       I

               F           A       H       C


       J   L       G
5 / 277

                                      K

   1. Fの親をBにする
                                      D


                          B       E       I

                  F           A       H       C


        J    L        G
6 / 277

                                      K

   1. Fの親をBにする
                                      D


                          B       E       I

                  F           A       H       C


        J    L        G
7 / 277

                                     K

   1. Eの親をDにする
                                     D


                         B       E       I

                 F           A       H       C


        J    L       G
8 / 277

                                     K

   1. Eの親をDにする
                                     D


                         B       E       I

                 F           A       H       C


        J    L       G
9 / 277

                                    K

   2. Iを親から切り離して根にする
                                    D


                        B       E       I

                F           A       H       C


        J   L       G
10 / 277

                                    K

   2. Iを親から切り離して根にする
                                    D


                        B       E       I

                F           A       H       C


        J   L       G
11 / 277

                                    K

   2. Lを親から切り離して根にする
                                    D


                        B       E       I

                F           A       H       C


        J   L       G
12 / 277

                                    K

   2. Lを親から切り離して根にする
                                    D


                        B       E       I

                F           A       H       C


        J   L       G
13 / 277

                                     K

   3. GとHのLCAを求める
                                     D


                         B       E       I

                 F           A       H       C


        J    L       G
14 / 277

                                        K

   3. GとHのLCAを求める
    ◦ LCA: 最小共通先祖
                                        D


                            B       E       I

                    F           A       H       C


         J     L        G
15 / 277




   頂点数 𝑁 ≤ 5000
   クエリ数 𝑄 ≤ 5000
16 / 277




   各頂点は親リンクを覚えておく
   クエリ1,2に対しては普通に答える


                 B

             F
17 / 277

                                   K

   各頂点は親リンクを覚えておく
   クエリ3に対しては                      D


                       B       E       I

               F           A       H       C


       J   L       G
18 / 277

                                     K

   各頂点は親リンクを覚えておく
   クエリ3に対しては                        D
    ◦ Gから根に向かって辿る

                         B       E       I

                 F           A       H       C


        J    L       G
19 / 277

                                     K

   各頂点は親リンクを覚えておく
   クエリ3に対しては                        D
    ◦ Gから根に向かって辿る
    ◦ Hから根に向かって辿る
                         B       E       I

                 F           A       H       C


        J    L       G
20 / 277

                                K

   各頂点は親リンクを覚えておく
   クエリ3に対しては                   D
    ◦ Gから根に向かって辿る
    ◦ Hから根に向かって辿る
    ◦ 並べる               B   E

               F                H


                    G
21 / 277




   各頂点は親リンクを覚えておく
   クエリ3に対しては
    ◦ Gから根に向かって辿る
    ◦ Hから根に向かって辿る
    ◦ 並べる


     K      D       B   F   G


     K      D       E   H
22 / 277




   各頂点は親リンクを覚えておく
   クエリ3に対しては
    ◦   Gから根に向かって辿る
    ◦   Hから根に向かって辿る
    ◦   根からの順番で並べる
    ◦   一致する中で最も後ろのものを選ぶ

        K     D     B      F   G


        K     D     E      H
23 / 277




   各頂点は親リンクを覚えておく
   クエリ3に対しては
    ◦   Gから根に向かって辿る
    ◦   Hから根に向かって辿る
    ◦   根からの順番で並べる
    ◦   根が一致しないときは-1
24 / 277




   頂点数 𝑁 ≤ 5000
   クエリ数 𝑄 ≤ 5000

   計算量は𝑂(𝑁𝑄)なので間に合う
25 / 277




   頂点数 𝑁 ≤ 106
   クエリ数 𝑄 ≤ 106
   辺の削除は行われない
26 / 277




   CがAとBのLCA
                    C



                    D       F


                A       B       E
27 / 277




   CがAとBのLCA
    ↓辺を追加           C



                    D       F


                A       B       E
28 / 277




   CがAとBのLCA
    ↓辺を追加            C
   Cは依然としてAとBのLCA


                     D       F


              A          B       E
29 / 277




   CがAとBのLCA
    ↓辺を追加
   Cは依然としてAとBのLCA

   最終的にできる木の上でLCAを計算できればよい
30 / 277




   木の嬉しい順序(DFS順序)
    ◦ Preorder –頂点に入るときに記録する順序

                                 A
   A, B, D, E, C, F
                            B        C

                        D        E       F
31 / 277




   木の嬉しい順序(DFS順序)
    ◦ Postorder – 頂点から出るときに記録する順序

                                 A
   D, E, B, F, C, A
                             B       C

                        D        E       F
32 / 277




   木の嬉しい順序(DFS順序)
    ◦ Euler Tour
      頂点から入るときに記録し、
      頂点から出るときにも
                               A
        自分の親を記録する順序
                           B       C

                       D       E       F
33 / 277




   木の嬉しい順序(DFS順序)
    ◦ Euler Tour
      頂点から入るときに記録し、
      頂点から出るときにも
                                A
        自分の親を記録する順序
                            B       C

                       D        E       F
                       A,
34 / 277




   木の嬉しい順序(DFS順序)
    ◦ Euler Tour
      頂点から入るときに記録し、
      頂点から出るときにも
                               A
        自分の親を記録する順序
                           B       C

                       D       E       F
                       A, B,
35 / 277




   木の嬉しい順序(DFS順序)
    ◦ Euler Tour
      頂点から入るときに記録し、
      頂点から出るときにも
                                  A
        自分の親を記録する順序
                           B          C

                       D          E       F
                       A, B, D,
36 / 277




   木の嬉しい順序(DFS順序)
    ◦ Euler Tour
      頂点から入るときに記録し、
      頂点から出るときにも
                                 A
        自分の親を記録する順序
                           B         C

                       D        E        F
                       A, B, D, B,
37 / 277




   木の嬉しい順序(DFS順序)
    ◦ Euler Tour
      頂点から入るときに記録し、
      頂点から出るときにも
                                 A
        自分の親を記録する順序
                           B           C

                       D        E          F
                       A, B, D, B, E
38 / 277




   木の嬉しい順序(DFS順序)
    ◦ Euler Tour
      頂点から入るときに記録し、
      頂点から出るときにも
                                  A
        自分の親を記録する順序
                            B              C

                       D         E             F
                       A, B, D, B, E, B,
39 / 277




   木の嬉しい順序(DFS順序)
    ◦ Euler Tour
      頂点から入るときに記録し、
      頂点から出るときにも
                                  A
        自分の親を記録する順序
                           B            C

                       D         E            F
                       A, B, D, B, E, B, A,
40 / 277




   木の嬉しい順序(DFS順序)
    ◦ Euler Tour
      頂点から入るときに記録し、
      頂点から出るときにも
                                  A
        自分の親を記録する順序
                           B            C

                       D         E            F
                       A, B, D, B, E, B, A,
                       C,
41 / 277




   木の嬉しい順序(DFS順序)
    ◦ Euler Tour
      頂点から入るときに記録し、
      頂点から出るときにも
                                  A
        自分の親を記録する順序
                           B            C

                       D         E            F
                       A, B, D, B, E, B, A,
                       C, F,
42 / 277




   木の嬉しい順序(DFS順序)
    ◦ Euler Tour
      頂点から入るときに記録し、
      頂点から出るときにも
                                  A
        自分の親を記録する順序
                           B            C

                       D         E            F
                       A, B, D, B, E, B, A,
                       C, F, C,
43 / 277




   木の嬉しい順序(DFS順序)
    ◦ Euler Tour
      頂点から入るときに記録し、
      頂点から出るときにも
                                  A
        自分の親を記録する順序
                           B            C

                       D         E            F
                       A, B, D, B, E, B, A,
                       C, F, C, A
44 / 277




   木の嬉しい順序(DFS順序)
    ◦ Euler Tour
      木の辺を、行きと帰りの2つの辺から
       なるとみなすときの                      A
       オイラー閉路に対応する

                               B            C

                       D             E            F
                           A, B, D, B, E, B, A,
                           C, F, C, A
45 / 277




   木の嬉しい順序(DFS順序)
    ◦ Euler Tour
      木の辺を、行きと帰りの2つの辺から
       なるとみなすときの
       オイラー閉路に対応する
      オイラー閉路 (Euler Tour) : 全ての辺を1度ずつ通る閉路
        オイラー路はケーニヒスベルクの橋問題で有名




                          Wikipediaより。 CC3.0-BY-SA
46 / 277




   Euler TourによるLCAの計算




                                  A

                              B       C

                          D       E       F
47 / 277




   Euler TourによるLCAの計算

      A   B   D   B   E   B   A   C   F   C       A
      1   2   3   2   3   2   1   2   3   2       1

                                                      A

                                              B           C

                                      D               E       F
48 / 277




   Euler TourによるLCAの計算

      A   B   D   B   E   B   A   C   F   C       A
      1   2   3   2   3   2   1   2   3   2       1

                                                      A

                                              B           C

                                      D               E       F
49 / 277




   Euler TourによるLCAの計算

      A   B   D   B   E   B   A   C   F   C       A
      1   2   3   2   3   2   1   2   3   2       1

                                                      A

                                              B           C

                                      D               E       F
50 / 277




   Euler TourによるLCAの計算

      A   B   D   B   E   B   A   C   F   C       A
      1   2   3   2   3   2   1   2   3   2       1

                                                      A

                                              B           C

                                      D               E       F
51 / 277




   Euler TourによるLCAの計算

      A   B   D   B   E   B   A   C   F   C       A
      1   2   3   2   3   2   1   2   3   2       1

                                                      A

                          深さ最小
                                              B           C

                                      D               E       F
52 / 277




   Euler TourによるLCAの計算

      A   B   D   B   E   B   A   C   F   C       A
      1   2   3   2   3   2   1   2   3   2       1

                                                      A
   RMQを利用                深さ最小
                                              B           C

                                      D               E       F
53 / 277




   正当性
54 / 277




   正当性: CがAとBのLCAのとき
    ◦ Euler Tour上でCは[A,B]に含まれる
    ◦ Euler Tour上で[A,B]に含まれるのはCの部分木
   を言えばよい
55 / 277




   正当性(1): Euler Tour上でCは[A,B]に含まれる
    ◦ AからBに行くにはCを経由しないといけない(LCAの性質より)
    ◦ ので当たり前
56 / 277




   正当性(2): Euler Tour上で[A,B]に含まれるのは
    Cの部分木
    ◦ Euler Tourにおいて部分木は連続した部分列として現れる
    ◦ ので当たり前
57 / 277




   LCAを求めて終わり?
58 / 277




   LCAを求めて終わり?
    ◦ あと少しだけやることがあります
59 / 277




   「削除がない場合の利点」の考察を思い出す
60 / 277




   「削除がない場合の利点」の考察を思い出す
   LCAが存在するなら、最終的な木の上で計算すれば
    よい
61 / 277




   「削除がない場合の利点」の考察を思い出す
   LCAが存在するなら、最終的な木の上で計算すれば
    よい
62 / 277




   A, Bが同じ木上にあるかどうかの判定が必要
63 / 277




   A, Bが同じ木上にあるかどうかの判定が必要

    ですが
64 / 277




   A, Bが同じ木上にあるかどうかの判定が必要

    ですが

    辺の追加クエリしかないのでUnionFindでよい
    ということはすぐにわかると思います
65 / 277




   頂点数 𝑁 ≤ 106
   クエリ数 𝑄 ≤ 106
   辺の削除は行われない

   𝑂(𝑁 + 𝑄 log 𝑁) なので間に合う
66 / 277




   ここまでの両方を実装すれば40点
67 / 277




   ここまでの両方を実装すれば40点

   複数のアルゴリズムを条件によって使い分けるテクは
    さすがに使っていると思います
68 / 277




   頂点数 𝑁 ≤ 106
   クエリ数 𝑄 ≤ 106
69 / 277




   頂点数 𝑁 ≤ 106
   クエリ数 𝑄 ≤ 106
   削除クエリもある
70 / 277




   追加も削除もある場合の頻出テク
71 / 277




   追加も削除もある場合の頻出テク
    ◦ クエリの(平方)分割
    ◦ がんばって動的になんとかする
72 / 277




   追加も削除もある場合の頻出テク

    ◦ がんばって動的になんとかする
73 / 277




   追加も削除もある場合の頻出テク

    ◦ がんばって動的になんとかする
74 / 277




   木が静的な場合のLCA (復習)
75 / 277




   木が静的な場合のLCA (復習)
    ◦ Euler Tour上で必要とされるクエリは以下の通り
76 / 277




   木が静的な場合のLCA (復習)
    ◦ Euler Tour上で必要とされるクエリは以下の通り
    1. 区間の最小値をとる
77 / 277




   木が静的な場合のLCA (復習)
    ◦ Euler Tour上で必要とされるクエリは以下の通り
    1. 区間の最小値をとる
    ◦ RMQで実現可能
78 / 277




   木が動的な場合のLCA (絶望)
    ◦ Euler Tour上で必要とされるクエリは以下の通り
    1. 区間の最小値をとる
    2.
    ◦ RMQで実現可能な気がする
79 / 277




   木が動的な場合のLCA (絶望)
    ◦ Euler Tour上で必要とされるクエリは以下の通り
    1. 区間の最小値をとる
    2. 列の連結をする
    3.
    ◦ RMQで実現可能な気がする
80 / 277




   木が動的な場合のLCA (絶望)
    ◦ Euler Tour上で必要とされるクエリは以下の通り
    1. 区間の最小値をとる
    2. 列の連結をする
    3. 列の分割をする
    4.
    ◦ RMQで実現可能な気がする
81 / 277




   木が動的な場合のLCA (絶望)
    ◦ Euler Tour上で必要とされるクエリは以下の通り
    1. 区間の最小値をとる
    2. 列の連結をする
    3. 列の分割をする
    4. それだけ?
82 / 277




   木が動的な場合のLCA (絶望)
    ◦ Euler Tour上で必要とされるクエリは以下の通り
    1. 区間の最小値をとる
    2. 列の連結をする
    3. 列の分割をする
    4. 区間に値を足す
83 / 277




   木が動的な場合のLCA (絶望)
    ◦ Euler Tour上で必要とされるクエリは以下の通り
    1. 区間の最小値をとる
    2. 列の連結をする
    3. 列の分割をする
    4. 区間に値を足す
    ◦ この業界では「Starry Sky木」として知られているもの
84 / 277




   木が動的な場合のLCA (絶望)
    ◦ Euler Tour上で必要とされるクエリは以下の通り
    1. 区間の最小値をとる
    2. 列の連結をする
    3. 列の分割をする
    4. 区間に値を足す
    ◦ この業界では「Starry Sky木」として知られているもの
      を平衡二分木として実装する必要がある (絶望)
85 / 277




   木が動的な場合のLCA (絶望)
    ◦ Euler Tour上で必要とされるクエリは以下の通り
    1. 区間の最小値をとる
    2. 列の連結をする
    3. 列の分割をする
    4. 区間に値を足す
    ◦ この業界では「Starry Sky木」として知られているもの
      を平衡二分木として実装する必要がある (絶望)
      しかもmerge/splitベースで
86 / 277




   平衡二分木の中身は後回し
87 / 277




   平衡二分木の中身は後回し
   平衡二分木を使った具体的な実装方法
88 / 277




   木の各頂点ごとに、Euler Tourのためのノードを2つ
    用意する(𝑆 𝐴 , 𝐺 𝐴 )
    ◦ 𝑆 𝐴 上には頂点Aの番号と、その深さが記録されている
    ◦ 𝐺 𝐴 上には頂点Aの親Pの番号と、その深さが記録されている
    ◦ Aが根のときは𝑆 𝐴 のみ使う
                       A        A
                                    𝐺𝐵
                           𝑆𝐴
                       B        B
89 / 277




   木の連結                                 A
                                            Depth 0



                            B                     C
                               Depth 0               Depth 1



                   D                 E  Depth 1
                                                         F
                       Depth 1
           𝑆𝐵 𝐵 , 𝑆𝐷   𝐷 , 𝐺 𝐷 𝐵 , 𝑆 𝐸 𝐸 , 𝐺 𝐸 (𝐵)          Depth 2
                                       𝑆 𝐴 𝐴 , 𝑆 𝐶 𝐶 , 𝑆 𝐹 𝐹 , 𝐺 𝐹 𝐶 , 𝐺 𝐹 (𝐴)
90 / 277




   木の連結                                            A
    1. Euler TourをA点で分割                               Depth 0
    ◦ A直下ならどの位置でもいい
      𝑆 𝐴 の直後がおすすめ
                                       B                    C
                                         Depth 0                Depth 1



                              D                 E Depth 1
                                                                  F
                                  Depth 1
                      𝑆𝐵 𝐵 , 𝑆𝐷   𝐷 , 𝐺 𝐷 𝐵 , 𝑆 𝐸 𝐸 , 𝐺 𝐸 (𝐵)       Depth 2
      𝑆 𝐴 𝐴 ,                            𝑆 𝐶 𝐶 , 𝑆 𝐹 𝐹 , 𝐺 𝐹 𝐶 , 𝐺 𝐹 (𝐴)
91 / 277




   木の連結                                                  A
    1. Euler TourをA点で分割                                      Depth 0
    2. Euler Tourを挿入

                                             B                     C
                                                Depth 0              Depth 1



                                     D                E                   F
                                        Depth 1          Depth 1            Depth 2
       𝑆 𝐴 𝐴 , 𝑆 𝐵 𝐵 , 𝑆 𝐷 𝐷 , 𝐺 𝐷 𝐵 , 𝑆 𝐸 𝐸 , 𝐺 𝐸 𝐵 , 𝐺 𝐵 (𝐸), 𝑆 𝐶 𝐶 , 𝑆 𝐹 𝐹 , 𝐺 𝐹 𝐶 , 𝐺 𝐹 (𝐴)
92 / 277




   木の連結                                                  A
    1. Euler TourをA点で分割                                      Depth 0
    2. Euler Tourを挿入
    3. 深さを調整
                                             B                     C
                                                Depth 1              Depth 1



                                     D                E                   F
                                        Depth 2          Depth 2            Depth 2
       𝑆 𝐴 𝐴 , 𝑆 𝐵 𝐵 , 𝑆 𝐷 𝐷 , 𝐺 𝐷 𝐵 , 𝑆 𝐸 𝐸 , 𝐺 𝐸 𝐵 , 𝐺 𝐵 (𝐸), 𝑆 𝐶 𝐶 , 𝑆 𝐹 𝐹 , 𝐺 𝐹 𝐶 , 𝐺 𝐹 (𝐴)
93 / 277




   木の削除: 追加のときと逆操作
94 / 277




   平衡Starry Sky Treeがあればよいことがわかった
95 / 277




   平衡Starry Sky Treeがあればよいことがわかった
   ではどのように実装するか?(実装例)
96 / 277




   今回は、葉ノードと内部ノードの区別をしない
    ◦ A,B,C,D,Eはどれも列上の項目とする


                           D


                       B       E


                   A       C
97 / 277




   各ノードは、Δ𝑥 𝐴 というフィールドを持つ


                         D


                     B       E


                 A       C
98 / 277




   各ノードは、Δ𝑥 𝐴 というフィールドを持つ
   各ノードに定めたい値𝑥 𝐴 は、
       𝑥𝐴 =                      Δ𝑥 𝐴
                                        D
              𝐴から根までのパス上のノード



                                  B              E


                             A          C
99 / 277




   𝑥 𝐴 = Δ𝑥 𝐷 + Δ𝑥 𝐵 + Δ𝑥 𝐴
   𝑥 𝐵 = Δ𝑥 𝐷 + Δ𝑥 𝐵
   𝑥 𝐶 = Δ𝑥 𝐷 + Δ𝑥 𝐵 + Δ𝑥 𝐶
   𝑥 𝐷 = Δ𝑥 𝐷                         D
   𝑥 𝐸 = Δ𝑥 𝐷 + Δ𝑥 𝐸

                                   B            E


                               A       C
100 / 277




   木の回転: 𝑥 𝐴 が保存されるように行う

   Δ𝑥 ′𝐴 =   𝑥𝐴 − 𝑥𝐵
   Δ𝑥 ′𝐵 =   𝑥𝐵                B
   Δ𝑥 ′𝐶 =   𝑥𝐶 − 𝑥𝐷
   Δ𝑥 ′𝐷 =   𝑥𝐷− 𝑥𝐵
   Δ𝑥 ′𝐸 =   𝑥𝐸− 𝑥𝐷        A        D


                                C         E
101 / 277




   木の回転: 𝑥 𝐴 が保存されるように行う

   Δ𝑥 ′𝐴 = Δ𝑥 𝐴
   Δ𝑥 ′𝐵 = Δ𝑥 𝐷 + Δ𝑥 𝐵         B
   Δ𝑥 ′𝐶 = Δ𝑥 𝐵 + Δ𝑥 𝐶
   Δ𝑥 ′𝐷 = −Δ𝑥 𝐵
   Δ𝑥 ′𝐸 = Δ𝑥 𝐸            A        D


                                C         E
102 / 277




   最後に、ノードに値𝑦 𝐴 を
   𝑦𝐴 =    min    𝑥𝐵 − 𝑥𝐴
         𝐴の全ての子孫 𝐵
   となるように計算して保持しておく
103 / 277




   最後に、ノードに値𝑦 𝐴 を
   𝑦𝐴 =    min    𝑥𝐵 − 𝑥𝐴
         𝐴の全ての子孫 𝐵
   となるように計算して保持しておく

   これでStarry Sky Tree相当の計算を行えるようになる
104 / 277




   平衡二分木の基本
105 / 277




   平衡二分木の基本:回転操作
106 / 277




   回転操作: 順序を保存したまま木構造を変形
107 / 277




   回転操作: 順序を保存したまま木構造を変形
   次のような二分木を考える
                       D


                     B         E


                 A       C
108 / 277




   回転操作: 順序を保存したまま木構造を変形
   次のような二分木を考える
   順序: 左の子孫→自分→右の子孫   D


                     B         E


                 A       C
109 / 277




   回転操作: 順序を保存したまま木構造を変形
   次のような二分木を考える
   順序: 左の子孫→自分→右の子孫   D

   この場合は A, B, C, D, E の順番
                              B         E


                          A       C
110 / 277




   次のように変形しても順番はA,B,C,D,E


                D            B


        B           E   A        D


    A       C                C       E
111 / 277




   次のように変形しても順番はA,B,C,D,E
   これを「木の回転」と言う

                D            B


        B           E   A        D


    A       C                C       E
112 / 277




   次のように変形しても順番はA,B,C,D,E
   これを「木の回転」と言う

   うまく回転をすることで、偏りが起きないようにする二
    分木を「平衡二分木」と言う
    ◦ 回転以外の方法で平衡を保つものもある
113 / 277




   平衡二分木の実装方法
114 / 277




   平衡二分木の実装方法

   今回は何でもOK!
    ◦   赤黒木
    ◦   RBST
    ◦   Treap
    ◦   Splay木
    ◦   などなど…
115 / 277




   この解説ではSplay木の説明をします
116 / 277




   突然ですが、Union Findの復習をします
117 / 277




   突然ですが、Union Findの復習をします
                                    A
   Union Find の効率化テクニック:
   アクセスした頂点を根へ持っていく
                            B                   F


                       C        D


                                        E
118 / 277




   突然ですが、Union Findの復習をします
                                    A
   Union Find の効率化テクニック:
   アクセスした頂点を根へ持っていく
                            B                   F


                       C        D


                                        E
119 / 277




   突然ですが、Union Findの復習をします
                                    A
   Union Find の効率化テクニック:
   アクセスした頂点を根へ持っていく
                            B                   F


                       C        D


                                        E
120 / 277




   突然ですが、Union Findの復習をします
                                    A
   Union Find の効率化テクニック:
   アクセスした頂点を根へ持っていく
                            B                   F


                       C        D


                                        E
121 / 277




   突然ですが、Union Findの復習をします
                                    A
   Union Find の効率化テクニック:
   アクセスした頂点を根へ持っていく
                            B                   F


                       C        D


                                        E
122 / 277




   突然ですが、Union Findの復習をします
                                    A
   Union Find の効率化テクニック:
   アクセスした頂点を根へ持っていく
                            B                   F


                       C        D


                                        E
123 / 277




   同じようなことを、二分探索木でもできないか?
124 / 277




   同じようなことを、二分探索木でもできないか?
    ◦ →Move-to-root heuristic
125 / 277




   Move-to-root heuristic
    ◦ 頂点にアクセスしたら、それが根に行くまで繰り返し回転する
126 / 277




   Move-to-root heuristic
    ◦ 頂点にアクセスしたら、それが根に行くまで繰り返し回転する

    ◦ そんなので上手くいくわけないだろ!!
127 / 277




   実際ダメ
128 / 277




   実際ダメ                   E


                       D



                   C


               B


           A
129 / 277




   実際ダメ                   E


                       D



                   C


               B


           A
130 / 277




   実際ダメ               E


                   D



               C


           A


               B
131 / 277




   実際ダメ               E


                   D



           A


               C


           B
132 / 277




   実際ダメ                   E


                   A


                       D


               C


           B
133 / 277




   実際ダメ               A


                           E


                   D


               C


           B
134 / 277




   実際ダメ
   この後A, B, C, D, Eの順にアクセスしたら
           𝑛 + 𝑛 − 1 + … + 1 = 𝑂 𝑛2
   のコストがかかってしまう
135 / 277




   実際ダメ
   この後A, B, C, D, Eの順にアクセスしたら
           𝑛 + 𝑛 − 1 + … + 1 = 𝑂 𝑛2
   のコストがかかってしまう

   どうする?
136 / 277




   解決策: 木の回転を3つに分ける
137 / 277




   解決策: 木の回転を3つに分ける
    ◦ “zig” step
    ◦ “zig-zag” step
    ◦ “zig-zig” step
138 / 277




   (1) “zig”-step
139 / 277




   (1) “zig”-step
   すぐ上が根の場合
                         R


                     A
140 / 277




   (1) “zig”-step
   すぐ上が根の場合
   普通に回転する          A


                         R
141 / 277




   (2) “zig-zag”-step
142 / 277




   (2) “zig-zag”-step
   左→右、またはその逆のとき
                             G


                    P


                         A
143 / 277




   (2) “zig-zag”-step
   左→右、またはその逆のとき
                          G
   普通に2回回転する

                      A


                  P
144 / 277




   (2) “zig-zag”-step
   左→右、またはその逆のとき
   普通に2回回転する          A


                  P        G
145 / 277




   (2) “zig-zag”-step
   左→右、またはその逆のとき
   普通に2回回転する

   ここまでは先ほどと同じ
146 / 277




   (3) “zig-zig”-step
147 / 277




   (3) “zig-zig”-step
   左→左、またはその逆のとき
                          G


                      P


                  A
148 / 277




   (3) “zig-zig”-step
   左→左、またはその逆のとき
                          G
   2回ではなく3回回転する

                      P


                  A
149 / 277




   (3) “zig-zig”-step
   左→左、またはその逆のとき
                             G
   2回ではなく3回回転する

                   A


                         P
150 / 277




   (3) “zig-zig”-step
   左→左、またはその逆のとき
                         A
   2回ではなく3回回転する

                             G


                         P
151 / 277




   (3) “zig-zig”-step
   左→左、またはその逆のとき
                         A
   2回ではなく3回回転する

                             P


                                 G

   (ただし、後でこれを2回として扱う)
152 / 277




   Splaying operation
    ◦ 偏った位置にあるときだけ余計に回転する
153 / 277




   Splaying operation
    ◦ 偏った位置にあるときだけ余計に回転する

    ◦ そんなので上手くいくわけないだろ!!
154 / 277




   実は上手くいく
155 / 277




   実は上手くいく

   具体的には: 𝑂(log 𝑁) amortized
156 / 277




   実は上手くいく

   具体的には: 𝑂(log 𝑁) amortized
157 / 277




   ならし計算量 (amortized time complexity)

   N個の一連の操作が𝑂(𝑓(𝑁))で行えるとする
                    𝑓 𝑁
 1つ1つの操作は、本当は𝑂(        )とは限らない
                      𝑁
        𝑓 𝑁
 これを𝑂(     )として扱うのが、ならし計算量
          𝑁
158 / 277




   ならし計算量のイメージ



                  操作1
      本当の計算量
                  操作2
                  操作3
                  操作4
      ならし計算量
                  操作5
159 / 277




   ならし計算量の向き/不向き
160 / 277




   ならし計算量の向き/不向き
   向いているもの
    ◦ 全体での処理効率が重視されるバッチ型の処理
    ◦ 例: プログラミングコンテスト
   向いていないもの
    ◦ リアルタイム性能が重視される処理
    ◦ 例: 信号処理
161 / 277




   ならし計算量と平均計算量
162 / 277




   ならし計算量と平均計算量
    ◦ この2つは別物!!
    ◦ ならし計算量 : 時系列上での平均
    ◦ 平均計算量 : 確率変数上での平均
163 / 277




   ならし計算量と平均計算量
    ◦ この2つは別物!!
    ◦ ならし計算量 : 時系列上での平均
    ◦ 平均計算量 : 確率変数上での平均

    ◦ ならし計算量 : 不確定要素は無い!
164 / 277




   Splayのならし計算量の評価
165 / 277




   Splayのならし計算量の評価

   「ポテンシャル関数」の概念を導入
166 / 277




   Splayのならし計算量の評価

   「ポテンシャル関数」の概念を導入
    ◦ 借金みたいなもの
167 / 277




   ポテンシャル関数を用いた計算量の均し(ならし)
   𝑎 𝑗 = 𝑡 𝑗 + Φ 𝑗+1 − Φj
    ◦ 𝑡 𝑗 : その操作の実際の計算量
    ◦ Φ 𝑗+1 − Φj : ポテンシャルの増加量
    ◦ 𝑎 𝑗 : その操作のならし計算量
168 / 277




   ポテンシャル関数を用いた計算量の均し(ならし)
   𝑎 𝑗 = 𝑡 𝑗 + Φ 𝑗+1 − Φj
    ◦ 𝑡 𝑗 : その操作の実際の計算量
    ◦ Φ 𝑗+1 − Φj : ポテンシャルの増加量
    ◦ 𝑎 𝑗 : その操作のならし計算量
   ポテンシャルの意味
    ◦ より大きい: 木はより偏っている
    ◦ より小さい: 木はより平坦になっている
169 / 277




   ならし計算量の総和をとる
    𝑗 𝑎𝑗 = 𝑗 𝑡 𝑗 + Φ 𝑚 − Φ0
    ◦ 𝑗 𝑡 𝑗 : 実際の計算量の総和
    ◦ Φ 𝑚 − Φ0 : ポテンシャルの総変化量
    ◦ 𝑗 𝑎 𝑗 : ならし計算量の総和
170 / 277




   ならし計算量の総和をとる
    𝑗 𝑎𝑗 = 𝑗 𝑡 𝑗 + Φ 𝑚 − Φ0
    ◦ 𝑗 𝑡 𝑗 : 実際の計算量の総和
    ◦ Φ 𝑚 − Φ0 : ポテンシャルの総変化量
    ◦ 𝑗 𝑎 𝑗 : ならし計算量の総和
   ポテンシャルの総変化量が小さければうまく評価でき
    る
171 / 277




   Splay木のポテンシャル
    ◦ Splay木の各頂点の重さを𝑤(𝑥)とする
      計算量の見積もり方にあわせて自由に決めてよい
    ◦ Splay木の頂点のサイズ s 𝑥 =                   𝑤(𝑦)
                              𝑥の全ての子孫 𝑦
    ◦ Splay木の頂点のランク 𝑟 𝑥 = log 2 𝑠(𝑥)
    ◦ Splay木のポテンシャル Φ =              𝑟(𝑥)
                         全ての頂点 𝑥
172 / 277




   Splay木のポテンシャル: 例
173 / 277




   Splay木のポテンシャル: 例
   重さ 𝑤(𝑥)
                           1
    (今回は全て1とする)


                   1               1


               1       1


                   1           1
174 / 277




   Splay木のポテンシャル: 例
   サイズ 𝑠(𝑥)
                             7
    ◦ 部分木の重さの和


                     5               1


                 1       3


                     1           1
175 / 277




   Splay木のポテンシャル: 例
   ランク 𝑟 𝑥 = log 2 𝑠(𝑥)
                                   2.8



                       2.3                0.0


                 0.0         1.6



                       0.0          0.0
176 / 277




   Splay木のポテンシャル: 例
   ポテンシャル: ランクの総和

          Φ = 2.8 + 2.3 + 1.6 = 6.7
177 / 277




   Splay木のポテンシャルの良い性質
178 / 277




   Splay木のポテンシャルの良い性質 …

   回転の影響を受ける頂点が少ない
    ◦ 解析が簡単になる
179 / 277




   アクセス補題 (Access Lemma)
180 / 277




   アクセス補題 (Access Lemma)

   𝑥 : 木のノード
   𝑡 : 木の根 とするとき

   木をsplayする操作一回にかかる時間(回転の回数)
    は、ならし計算量で
              3𝑟 𝑡 − 3𝑟 𝑥 + 1
   以下である。
181 / 277




   アクセス補題の証明
182 / 277




   アクセス補題の証明

   各回転ステップのならし計算量が
    1. “zig”-stepでは 3𝑟 ′ 𝑥 − 3𝑟 𝑥 + 1 以下
    2. それ以外では 3𝑟 ′ 𝑥 − 3𝑟(𝑥) 以下
   (ただし、𝑟′(𝑥) : 操作後のランク)
   であることを示す。
   そうすると、1のケースに登場する𝑟′(𝑥)は初期の𝑟(𝑡)
    と等しい(木全体のサイズの対数)ので、合計すると
    3𝑟 𝑡 − 3𝑟 𝑥 + 1になる。
183 / 277




   アクセス補題の証明 (1) “zig”-step の場合
   𝑟 𝑋 = log 2 (𝑤 𝑋 + 𝑤 𝐴 + 𝑤 𝐵 )
                                   R

   𝑟 𝑅 = log 2 (𝑤 𝑋 + 𝑤 𝐴 + 𝑤 𝐵 + 𝑤 𝐶 + 𝑤(𝑅))
                              X         C


                          A       B
184 / 277




   アクセス補題の証明 (1) “zig”-step の場合
   𝑟 𝑋 = log 2 (𝑤 𝑋 + 𝑤 𝐴 + 𝑤 𝐵 )X
   𝑟 ′ 𝑋 = log 2 (𝑤 𝑋 + 𝑤 𝐴 + 𝑤 𝐵 + 𝑤 𝐶 + 𝑤 𝑅 )
   𝑟 𝑅 = log 2 (𝑤 𝑋 + 𝑤 𝐴 + 𝑤 𝐵 + 𝑤 𝐶 + 𝑤(𝑅))
   𝑟 ′ 𝑅 = log 2 (𝑤 𝑅 + 𝑤 𝐵 + A 𝐶 ) R
                               𝑤


                                 B       C
185 / 277




   アクセス補題の証明 (1) “zig”-step の場合
   𝑟 𝑋 = log 2 (𝑤 𝑋 + 𝑤 𝐴 + 𝑤 𝐵 )
   𝑟 ′ 𝑋 = log 2 (𝑤 𝑋 + 𝑤 𝐴 + 𝑤 𝐵 + 𝑤 𝐶 + 𝑤 𝑅 )
   𝑟 𝑅 = log 2 (𝑤 𝑋 + 𝑤 𝐴 + 𝑤 𝐵 + 𝑤 𝐶 + 𝑤(𝑅))
   𝑟 ′ 𝑅 = log 2 (𝑤 𝑅 + 𝑤 𝐵 + 𝑤 𝐶 )
186 / 277




   アクセス補題の証明 (1) “zig”-step の場合
   𝑟 𝑋 = log 2 (𝑤 𝑋 + 𝑤 𝐴 + 𝑤 𝐵 )
   𝑟 ′ 𝑋 = log 2 (𝑤 𝑋 + 𝑤 𝐴 + 𝑤 𝐵 + 𝑤 𝐶 + 𝑤 𝑅 )
   𝑟 𝑅 = log 2 (𝑤 𝑋 + 𝑤 𝐴 + 𝑤 𝐵 + 𝑤 𝐶 + 𝑤(𝑅))
   𝑟 ′ 𝑅 = log 2 (𝑤 𝑅 + 𝑤 𝐵 + 𝑤 𝐶 )

   𝑟 𝑋 ≤ 𝑟 ′ 𝑋 , 𝑟 ′ 𝑅 ≤ 𝑟(𝑅)
187 / 277




   アクセス補題の証明 (1) “zig”-step の場合
   𝑟 𝑋 ≤ 𝑟 ′ 𝑋 , 𝑟 ′ 𝑅 ≤ 𝑟(𝑅)
   ならし計算量

        𝑎 = 𝑡 + Φ′ − Φ
        = 1 + 𝑟′ 𝑋 + 𝑟′ 𝑅 − 𝑟 𝑋 − 𝑟 𝑅
              ≤ 1 + 𝑟′ 𝑋 − 𝑟 𝑋
              ≤ 1 + 3𝑟 ′ 𝑋 − 3𝑟 𝑋
188 / 277




   アクセス補題の証明 (2) “zigzig”-step の場合
   𝑟 𝑋 = log 2 (𝑤 𝑋 + 𝑠 𝐴 + 𝑠 𝐵 ) G

                                           D
    𝑟 𝑃 = log 2 (𝑤 𝑋 + 𝑠 𝐴 + 𝑠 𝐵 + 𝑠 𝐶P + 𝑤(𝑃))

    𝑟 𝐺 =                        X     C
    log 2 (𝑤 𝑋 + 𝑠 𝐴 + 𝑠 𝐵 + 𝑠 𝐶 + 𝑤 𝑃 + 𝑠 𝐷 + 𝑤 𝐺 )
                              A     B
189 / 277




   アクセス補題の証明 (2) “zigzig”-step の場合
    𝑟 𝑋 = log 2 (𝑤 𝑋 + 𝑠 𝐴 + 𝑠 𝐵 )      X
       ′ 𝑋 =
    𝑟
    log 2 (𝑤 𝑋 + 𝑠 𝐴 + 𝑠 𝐵 + 𝑠 𝐶 + 𝑠 𝐷 + 𝑤 𝑃 + 𝑤 𝐺 )
                                      A     P
    𝑟 𝑃 = log 2 (𝑤 𝑋 + 𝑠 𝐴 + 𝑠 𝐵 + 𝑠 𝐶 + 𝑤(𝑃))
    𝑟 ′ 𝑃 = log 2 (𝑠 𝐵 + 𝑠 𝐶 + 𝑤 𝑃 + 𝑤 𝐺 )
                                        B     G
    𝑟 𝐺 =
    log 2 (𝑤 𝑋 + 𝑠 𝐴 + 𝑠 𝐵 + 𝑠 𝐶 + 𝑤 𝑃 + 𝑠 𝐷 + 𝑤 𝐺 )
    𝑟 ′ 𝐺 = log 2 (𝑠 𝐶 + 𝑠 𝐷 + 𝑤 𝐺 )       C   D
190 / 277




   アクセス補題の証明 (2) “zigzig”-step の場合
    𝑟 𝑋 = log 2 (𝑤 𝑋 + 𝑠 𝐴 + 𝑠 𝐵 )
    𝑟′ 𝑋 =
    log 2 (𝑤 𝑋 + 𝑠 𝐴 + 𝑠 𝐵 + 𝑠 𝐶 + 𝑠 𝐷 + 𝑤 𝑃 + 𝑤 𝐺 )
    𝑟 𝑃 = log 2 (𝑤 𝑋 + 𝑠 𝐴 + 𝑠 𝐵 + 𝑠 𝐶 + 𝑤(𝑃))
    𝑟 ′ 𝑃 = log 2 (𝑠 𝐵 + 𝑠 𝐶 + 𝑤 𝑃 + 𝑤 𝐺 )
    𝑟 𝐺 =
    log 2 (𝑤 𝑋 + 𝑠 𝐴 + 𝑠 𝐵 + 𝑠 𝐶 + 𝑤 𝑃 + 𝑠 𝐷 + 𝑤 𝐺 )
    𝑟 ′ 𝐺 = log 2 (𝑠 𝐶 + 𝑠 𝐷 + 𝑤 𝐺 )
191 / 277




   アクセス補題の証明 (2) “zigzig”-step の場合
   𝑟′ 𝑋 = 𝑟 𝐺 , 𝑟′ 𝑋 ≤ 𝑟′ 𝑃 , 𝑟 𝑃 ≤ 𝑟 𝑋
192 / 277




   アクセス補題の証明 (2) “zigzig”-step の場合
   𝑟′ 𝑋 = 𝑟 𝐺 , 𝑟′ 𝑃 ≤ 𝑟′ 𝑋 , 𝑟 𝑋 ≤ 𝑟 𝑃

    𝑎 = 𝑡 + Φ′ − Φ
          = 2 + 𝑟′ 𝑋 + 𝑟′ 𝑃 + 𝑟′ 𝐺 − 𝑟 𝑋 − 𝑟 𝑃
          − 𝑟 𝐺
          ≤ 2 + 𝑟 ′ 𝑋 + 𝑟 ′ 𝐺 − 2𝑟 𝑋
193 / 277




   アクセス補題の証明 (2) “zigzig”-step の場合
   2 + 𝑟 ′ 𝑋 + 𝑟 ′ 𝐺 − 2𝑟 𝑋 ≤ 3𝑟 ′ 𝑋 − 3𝑟 𝑋
   理由: 𝑟 ′ 𝐺 + 𝑟 𝑋 − 2𝑟 ′ 𝑋 ≤ −2 を示したい。
                     𝑠′ 𝐺              𝑠 𝑋
   ところで左辺はlog 2            + log 2          であり、
                     𝑠′ 𝑋             𝑠′ 𝑋
   log 2 𝑥が上に凸で、𝑠 ′ 𝐺 + 𝑠 𝑋 ≤ 𝑠′(𝑋)なのでこの
    値は高々−2
   よって不等式は示された。
194 / 277




   アクセス補題の証明 (3) “zigzag”-step の場合
   𝑟′ 𝑋 = 𝑟 𝐺 , 𝑟 𝑋 ≤ 𝑟 𝑃
   𝑠′ 𝑃 + 𝑠′ 𝐺 ≤ 𝑠′ 𝑋
              G
                                   X
          P       D

                           P               G
      A       X
                       A       B       C       D
          B       C
195 / 277




   アクセス補題の証明 (3) “zigzag”-step の場合
   以下(2)と同様
196 / 277




   以上より、Splay操作がならし計算量で𝑂(log 𝑁)であ
    ることがわかった。
   ところで、Splay操作のポテンシャルは高々
    𝑂(𝑁 log 𝑁 )なので、全体で𝑂((𝑄 + 𝑁) log 𝑁)でクエリ
    を処理できることがわかった。
197 / 277




   以上より、Splay操作がならし計算量で𝑂(log 𝑁)であ
    ることがわかった。
   ところで、Splay操作のポテンシャルは高々
    𝑂(𝑁 log 𝑁 )なので、全体で𝑂((𝑄 + 𝑁) log 𝑁)でクエリ
    を処理できることがわかった。

   以上、満点解法その1
198 / 277




   満点解法その2 - Link/Cut木
199 / 277




   満点解法その2 - Link/Cut木

   Link/Cut木
    ◦ 元々、フローアルゴリズムの高速化のためにSleatorとTarjan
      が考案したもの
200 / 277




   満点解法その2 - Link/Cut木

   Link/Cut木
    ◦ 元々、フローアルゴリズムの高速化のためにSleatorとTarjan
      が考案したもの
    ◦ この問題のために必要な実装は、それよりもはるかに容易
201 / 277




   満点解法その2 - Link/Cut木

   Link/Cut木
    ◦ 元々、フローアルゴリズムの高速化のためにSleatorとTarjan
      が考案したもの
    ◦ この問題のために必要な実装は、それよりもはるかに容易
      →Link/Cut木の練習としても適している
202 / 277




   満点解法その2 - Link/Cut木

   Link/Cut木
    ◦ 元々、フローアルゴリズムの高速化のためにSleatorとTarjan
      が考案したもの
    ◦ この問題のために必要な実装は、それよりもはるかに容易
      →Link/Cut木の練習としても適している
    ◦ いろいろなバージョンがあるが、Splay木によるものが使いや
      すい
203 / 277




   予備知識 – Heavy/Light decomposition
204 / 277




   予備知識 – Heavy/Light decomposition
    ◦ 木をパスに分割する方法
                         A


                 B           C


             D       E           F
205 / 277




   予備知識 – Heavy/Light decomposition
    ◦ 木をパスに分割する方法
                         A


                 B           C


             D       E           F
206 / 277




   予備知識 – Heavy/Light decomposition
    ◦ 木をパスに分割する方法

   変な形の木でも、「パスの木」の形に潰すと安定する
207 / 277




   Splay Treeの世界




        列データ           Splay Tree

                    列の畳み込みを効率よく計算
208 / 277




   Splay Treeの世界




        列データ           Splay Tree

                    列の畳み込みを効率よく計算
209 / 277




   Heavy/Light decompositionの世界

         有向木




         列データ                      Splay Tree

     有向木を分解したもの              列の畳み込みを効率よく計算
210 / 277




   Link/Cut Treeの世界

         有向木              Link/Cut Tree

                       パスの畳み込みを効率よく計算




        列データ               Splay Tree

     有向木を分解したもの        列の畳み込みを効率よく計算
211 / 277




   Link/Cut Treeの世界

   H/L分解 = パスからなる木
212 / 277




   Link/Cut Treeの世界

   H/L分解 = パスからなる木
   Link/Cut Tree = Splay木からなる木
213 / 277




   Link/Cut Tree の辺は二種類ある
    ◦ Solid(Heavy) edge
    ◦ Dashed(Light) edge
214 / 277




   Link/Cut Tree の辺は二種類ある

               Solid(Heavy)   Dashed(Light)

     所属        Splay Tree     H/L分解の木

     分類        二分木            多分木

     左右の区別     左右の区別あり        なし

     親         本当は祖先か子孫       本当の親

     子供        本当は祖先か子孫       本当は子孫
215 / 277




   Solid, Dashedの区別
216 / 277




   Solid, Dashedの区別

   いずれも、親方向リンクを持つ


                               X



                       L               R
                           M
217 / 277




   Solid, Dashedの区別

   いずれも、親方向リンクを持つ
   親から左方向リンクがあれば、Solid
                               X



                       L               R
                           M
218 / 277




   Solid, Dashedの区別

   いずれも、親方向リンクを持つ
   親から左方向リンクがあれば、Solid
   親から右方向リンクがあれば、Solid
                               X



                       L               R
                           M
219 / 277




   Solid, Dashedの区別

   いずれも、親方向リンクを持つ
   親から左方向リンクがあれば、Solid
   親から右方向リンクがあれば、Solid
                               X
   どちらもなければ、Dashed


                       L               R
                           M
220 / 277




   Solid, Dashedの区別

   いずれも、親方向リンクを持つ
   親から左方向リンクがあれば、Solid
   親から右方向リンクがあれば、Solid
   どちらもなければ、Dashed

   「右, 左, 親」の3つのリンクだけで構造を保持できる!
221 / 277




   小さな例               A


                   B


               C       F


           D       E
222 / 277




   小さな例               A


                   B


               C       F


           D       E
223 / 277




       小さな例
                F   B   A
            A

        B
                    D   C

    C       F

D       E
                        E
224 / 277




       小さな例            B
            A

        B
                    F           A

    C       F

D       E
                        D

F       B       A
                            C
        D       C

                E
                        E
225 / 277




   Link/Cut Treeの操作: splayLC()

   Link/Cut Tree上では、任意の頂点Xを根に持っていく
    ことができる
    ◦ 元の木の構造は変化しないことに注意
226 / 277




   前準備: splay

   各Splay Tree上でsplayをすることで、Xから今の根ま
    でを点線だけで行けるようにする
227 / 277




   前準備: splay     B

   各Splay Tree上でsplayをすることで、Xから今の根ま
                F          A
    でを点線だけで行けるようにする

                  D

                       C


                   E
228 / 277




   前準備: splay       B

   各Splay Tree上でsplayをすることで、Xから今の根ま
                F          A
    でを点線だけで行けるようにする

                         C

                 D


                     E
229 / 277




   前準備: splay       B

   各Splay Tree上でsplayをすることで、Xから今の根ま
                F          A
    でを点線だけで行けるようにする

                         C   EからBまで、点
                             線のみで行ける
                 D             ようになる


                     E
230 / 277




   splayLC() のメイン操作: つなぎ変え(expose操作)

   パスのつなぎ変え操作を行う

   元の木ではこんな感じ
231 / 277




                       A
    splayLC() のメイン操作: つなぎ変え(expose操作)

             B
    パスのつなぎ変え操作を行う

   元の木ではこんな感じ
            C          F


             D     E
232 / 277




                       A
    splayLC() のメイン操作: つなぎ変え(expose操作)

             B
    パスのつなぎ変え操作を行う

   元の木ではこんな感じ
            C          F


             D     E
233 / 277




                       A
    splayLC() のメイン操作: つなぎ変え(expose操作)

             B
    パスのつなぎ変え操作を行う

   元の木ではこんな感じ
            C          F


             D     E
234 / 277




   splayLC() のメイン操作: つなぎ変え(expose操作)

   パスのつなぎ変え操作を行う

   Link/Cut 木でも、左向きのSolid辺を付け替えるだけ
235 / 277




   splayLC() のメイン操作: B
                      つなぎ変え(expose操作)

          F
    パスのつなぎ変え操作を行う         A

   Link/Cut 木でも、左向きのSolid辺を付け替えるだけ
                     C

                D


                    E
236 / 277




   splayLC() のメイン操作: B
                      つなぎ変え(expose操作)

          F
    パスのつなぎ変え操作を行う         A

   Link/Cut 木でも、左向きのSolid辺を付け替えるだけ
                     C

                D


                    E
237 / 277




                       B
   splayLC() のメイン操作: つなぎ変え(expose操作)

            C
    パスのつなぎ変え操作を行う          A

             E
    Link/Cut 木でも、左向きのSolid辺を付け替えるだけ




             D             F
238 / 277




   左向きの辺をつなぎ替えるだけで、Eが一番上の木に
    所属するようになった
239 / 277




   左向きの辺をつなぎ替えるだけで、Eが一番上の木に
    所属するようになった

   最後にもう1度splay()操作を行うことで、Link/Cut
    Treeの一番上の根にEが来る
240 / 277




   L/C木のsplayLC()はSplay Treeの解析を少し応用す
    ると、対数時間であることが言える
241 / 277




   L/C木のsplayLC()はSplay Treeの解析を少し応用す
    ると、対数時間であることが言える

   1回ごとのsplay()操作が対数時間であることは既に
    わかっている
242 / 277




   L/C木のsplayLC()はSplay Treeの解析を少し応用す
    ると、対数時間であることが言える

   1回ごとのsplay()操作が対数時間であることは既に
    わかっている
   しかし実際にはsplay()操作が𝑘回呼ばれている
    ◦ 𝑘は、パス分割された木の上での深さ
243 / 277




   ポテンシャルの定義
    ◦ サイズ = solid/dashedに関わらず、子孫になっている頂点の
      数
    ◦ ランク = その対数
    ◦ ポテンシャル = ランクの総和の2倍
      として定める
244 / 277




   Splay Treeのならし計算量は 1 + 3𝑟 𝑡 − 3𝑟(𝑥) だっ
    た
   今回のならし計算量は𝑘 + 6𝑟 𝑡 − 6𝑟(𝑥)になる
    ◦ 「Splayが𝑘回呼ばれる」という認識を改めてみる
    ◦ Splayは根に向かって順番に呼ばれるということを考慮すると、
      「Splayが1回呼ばれるが、途中でk回、強制的にzigステップを
      使われるかもしれない」と考えることができる
    ◦ 係数が2倍なのはポテンシャルの定義を変えたから
245 / 277




   余った定数項𝑘の回収

   Expose操作のあとに1回行うsplay操作: k回の回転を
    行う。
   ポテンシャルの定義を2倍にしたので、splayの回転操
    作1回につき1の追加コストを課しても問題ない
246 / 277




   ならしコスト6 log 2 𝑁のsplay操作を2回呼んでいるの
    で、splayLC()のならし計算量は12 log 2 𝑁 = 𝑂(log 𝑁)
    であるとわかった。
247 / 277




   AとBのLCAを求める。
248 / 277




   AとBのLCAを求めるには、まず
    1. Bに対してsplayLC()を行う
    2. Aに対してsplayLC()を行う
   このとき、Bは浅い位置にいる。
    ◦ Splay Treeに対するSplay操作1回で、他の頂点の深さは高々
      2段しか下がらないので、この時点でBは深さ高々4程度。
249 / 277




   AとBの位置関係に基いて条件分岐
250 / 277




   AとBの位置関係に基いて条件分岐
   (1) BがAの左側にある場合
    ◦ この場合は、BはAの子孫ということになるので、AとBのLCA
      はAになる。
   (2) BがAの右側にある場合
    ◦ 次のページへ
251 / 277




   BがAの右側にある場合の条件分岐
   (1) BがAと同じSplay Treeに属する場合
    ◦ この場合は、AはBの子孫ということになるので、AとBのLCA
      はBになる。
252 / 277




   BがAの右側にある場合の条件分岐
   (1) BがAと同じSplay Treeに属する場合
    ◦ この場合は、AはBの子孫ということになるので、AとBのLCA
      はBになる。
   (2) BがAと異なるSplay Treeに属する場合
    ◦ 一番一般的な場合。
    ◦ Bから上に辿り、Aと同じSplay Treeに到達したところの頂点が、
      AとBのLCAになる。
253 / 277




   BがAの右側にある場合の条件分岐
   (1) BがAと同じSplay Treeに属する場合
    ◦ この場合は、AはBの子孫ということになるので、AとBのLCA
      はBになる。
   (2) BがAと異なるSplay Treeに属する場合
    ◦ 一番一般的な場合。
    ◦ Bから上に辿り、Aと同じSplay Treeに到達したところの頂点が、
      AとBのLCAになる。


   これでLCAは求められた。
254 / 277




   クエリ1,2番に対応する「接続」「切断」は、
    Link/Cut Treeの”link”, “cut” に対応する。
255 / 277




   クエリ1,2番に対応する「接続」「切断」は、
    Link/Cut Treeの”link”, “cut” に対応する。
   (1) Link操作 – AをBの子にする
    ◦ AとBをsplayLC()しておいてから、Aの親として(dashedで)Bを
      設定するだけ。
    ◦ 計算量: AとBがLink/Cut Treeにおける根にあるので、Bのサ
      イズが高々𝑁増える程度。これによってポテンシャルは
       𝑂(log 𝑁) しか増えない。
256 / 277




   クエリ1,2番に対応する「接続」「切断」は、
    Link/Cut Treeの”link”, “cut” に対応する。
   (2) Cut操作 – Aを親から切り離す
    ◦ AをsplayLC()してからAの右の子を切り離す。
    ◦ 計算量:ポテンシャルは明らかに減っている。
257 / 277




   以上がLink/Cut Treeによる満点解法。
258 / 277




   Euler Tour Tree と Link/Cut Tree は動的木の筆頭
259 / 277




   Euler Tour Tree と Link/Cut Tree は動的木の筆頭
   今回はどちらを選ぶべきだったか?
260 / 277




   Euler Tour Tree と Link/Cut Tree は動的木の筆頭
   今回はどちらを選ぶべきだったか?

   (他の問題は解き終わっているとして)
261 / 277




   Euler Tour Tree
    ◦ 知識:
    ◦ 実装:



   Link/Cut Tree
    ◦ 知識:
    ◦ 実装:
262 / 277




   Euler Tour Tree
    ◦ 知識: 過去にも出題済みの知識の組合せ。
    ◦ 実装:



   Link/Cut Tree
    ◦ 知識:
    ◦ 実装:
263 / 277




   Euler Tour Tree
    ◦ 知識: 過去にも出題済みの知識の組合せ。
    ◦ 実装: 組み合わせてはいけないものを組み合わせてしまった
      感じ


   Link/Cut Tree
    ◦ 知識:
    ◦ 実装:
264 / 277




   Euler Tour Tree
    ◦ 知識: 過去にも出題済みの知識の組合せ。
    ◦ 実装: 組み合わせてはいけないものを組み合わせてしまった
      感じ


   Link/Cut Tree
    ◦ 知識: 必須
    ◦ 実装:
265 / 277




   Euler Tour Tree
    ◦ 知識: 過去にも出題済みの知識の組合せ。
    ◦ 実装: 組み合わせてはいけないものを組み合わせてしまった
      感じ


   Link/Cut Tree
    ◦ 知識: 必須
    ◦ 実装: 頂点にデータを持たせなくてよいなど、この問題におい
      ては極めて有利
266 / 277




   Euler Tour Tree
    ◦ 知識: 過去にも出題済みの知識の組合せ。
    ◦ 実装: 組み合わせてはいけないものを組み合わせてしまった
      感じ

   Link/Cut Tree
    ◦ 知識: 必須
    ◦ 実装: 頂点にデータを持たせなくてよいなど、この問題におい
      ては極めて有利
   知っているならLink/Cut を書くべきだったかもしれな
    い
267 / 277




   Link/Cutを学ぶべきか?
268 / 277




   Link/Cutを学ぶべきか?
    ◦ Link/Cutでなければ出来ない、という問題は、恐らくない
269 / 277




   Link/Cutを学ぶべきか?
    ◦ Link/Cutでなければ出来ない、という問題は、恐らくない

    ◦ しかし、Link/Cutを使うと有利な問題は実際に存在している
270 / 277




   qnighyからの提案
271 / 277




   qnighyからの提案
    ◦ 合宿参加者の大半にとっては、Link/Cut Treeを習得するコ
      ストが高くつく上に、他の学習をしたほうがずっと為になると
      思う。
272 / 277




   qnighyからの提案
    ◦ 合宿参加者の大半にとっては、Link/Cut Treeを習得するコ
      ストが高くつく上に、他の学習をしたほうがずっと為になると
      思う。
    ◦ より上位の人や、単純に興味があるという人に関しては、こ
      の限りではない。
273 / 277




   qnighyからの提案
    ◦ 合宿参加者の大半にとっては、Link/Cut Treeを習得するコ
      ストが高くつく上に、他の学習をしたほうがずっと為になると
      思う。
    ◦ より上位の人や、単純に興味があるという人に関しては、こ
      の限りではない。
    ◦ いずれにせよ、学習するつもりなら、身に付けるために問題
      を解くべきだろう。
274 / 277




   JOI2010春合宿 Day4 “Highway”
   JOI2012本選 問題5 “Festivals in JOI Kingdom”
   IOI2011 Day2 “Elephants”
   IJPC2012 Day3 “Animals2”
275 / 277




   完全制覇・ツリー上でのクエリ処理技法 [iwiwi]
    http://topcoder.g.hatena.ne.jp/iwiwi/20111205/13
    23099376
   プログラミングコンテストでのデータ構造 2 ~動的木
    編~ [iwiwi]
    http://www.slideshare.net/iwiwi/2-12188845
   蟻本 [iwiwi]
276 / 277




   Daniel D. Sleator and Robert E. Tarjan, A Data
    Structure for Dynamic Trees, Journal of Computer
    and System Sciences, Volume 26 Issue 3, June 1983,
    pp. 362 – 391
   Daniel D. Sleator and Robert E. Tarjan, Self-adjusting
    binary search trees, Journal of the ACM, Volume 32
    Issue 3, July 1985, pp. 652 – 686
277 / 277




12

10

8

6

4

2

0
     0   10   20   30   40   50   60   70   80   90     100

Spaceships 解説

  • 1.
    1 / 277 Masaki Hara (qnighy) 2013年 情報オリンピック春期トレーニング合宿にて
  • 2.
    2 / 277 有向木がたくさんあります  以下のクエリに高速で答えてください 1. Aの親をBにする ◦ Aは木の根で、BはAの子孫ではない 2. Aを親から切り離して木の根にする 3. A,Bが同じ木に属するか判定し、 同じ木に属する場合はLCAを求める
  • 3.
    3 / 277 K  有向木がたくさんあります D B E I F A H C J L G
  • 4.
    4 / 277 K  有向木がたくさんあります  以下のクエリに高速で答えてください D B E I F A H C J L G
  • 5.
    5 / 277 K  1. Fの親をBにする D B E I F A H C J L G
  • 6.
    6 / 277 K  1. Fの親をBにする D B E I F A H C J L G
  • 7.
    7 / 277 K  1. Eの親をDにする D B E I F A H C J L G
  • 8.
    8 / 277 K  1. Eの親をDにする D B E I F A H C J L G
  • 9.
    9 / 277 K  2. Iを親から切り離して根にする D B E I F A H C J L G
  • 10.
    10 / 277 K  2. Iを親から切り離して根にする D B E I F A H C J L G
  • 11.
    11 / 277 K  2. Lを親から切り離して根にする D B E I F A H C J L G
  • 12.
    12 / 277 K  2. Lを親から切り離して根にする D B E I F A H C J L G
  • 13.
    13 / 277 K  3. GとHのLCAを求める D B E I F A H C J L G
  • 14.
    14 / 277 K  3. GとHのLCAを求める ◦ LCA: 最小共通先祖 D B E I F A H C J L G
  • 15.
    15 / 277  頂点数 𝑁 ≤ 5000  クエリ数 𝑄 ≤ 5000
  • 16.
    16 / 277  各頂点は親リンクを覚えておく  クエリ1,2に対しては普通に答える B F
  • 17.
    17 / 277 K  各頂点は親リンクを覚えておく  クエリ3に対しては D B E I F A H C J L G
  • 18.
    18 / 277 K  各頂点は親リンクを覚えておく  クエリ3に対しては D ◦ Gから根に向かって辿る B E I F A H C J L G
  • 19.
    19 / 277 K  各頂点は親リンクを覚えておく  クエリ3に対しては D ◦ Gから根に向かって辿る ◦ Hから根に向かって辿る B E I F A H C J L G
  • 20.
    20 / 277 K  各頂点は親リンクを覚えておく  クエリ3に対しては D ◦ Gから根に向かって辿る ◦ Hから根に向かって辿る ◦ 並べる B E F H G
  • 21.
    21 / 277  各頂点は親リンクを覚えておく  クエリ3に対しては ◦ Gから根に向かって辿る ◦ Hから根に向かって辿る ◦ 並べる K D B F G K D E H
  • 22.
    22 / 277  各頂点は親リンクを覚えておく  クエリ3に対しては ◦ Gから根に向かって辿る ◦ Hから根に向かって辿る ◦ 根からの順番で並べる ◦ 一致する中で最も後ろのものを選ぶ K D B F G K D E H
  • 23.
    23 / 277  各頂点は親リンクを覚えておく  クエリ3に対しては ◦ Gから根に向かって辿る ◦ Hから根に向かって辿る ◦ 根からの順番で並べる ◦ 根が一致しないときは-1
  • 24.
    24 / 277  頂点数 𝑁 ≤ 5000  クエリ数 𝑄 ≤ 5000  計算量は𝑂(𝑁𝑄)なので間に合う
  • 25.
    25 / 277  頂点数 𝑁 ≤ 106  クエリ数 𝑄 ≤ 106  辺の削除は行われない
  • 26.
    26 / 277  CがAとBのLCA C D F A B E
  • 27.
    27 / 277  CがAとBのLCA ↓辺を追加 C D F A B E
  • 28.
    28 / 277  CがAとBのLCA ↓辺を追加 C  Cは依然としてAとBのLCA D F A B E
  • 29.
    29 / 277  CがAとBのLCA ↓辺を追加  Cは依然としてAとBのLCA  最終的にできる木の上でLCAを計算できればよい
  • 30.
    30 / 277  木の嬉しい順序(DFS順序) ◦ Preorder –頂点に入るときに記録する順序 A  A, B, D, E, C, F B C D E F
  • 31.
    31 / 277  木の嬉しい順序(DFS順序) ◦ Postorder – 頂点から出るときに記録する順序 A  D, E, B, F, C, A B C D E F
  • 32.
    32 / 277  木の嬉しい順序(DFS順序) ◦ Euler Tour  頂点から入るときに記録し、  頂点から出るときにも A 自分の親を記録する順序 B C D E F
  • 33.
    33 / 277  木の嬉しい順序(DFS順序) ◦ Euler Tour  頂点から入るときに記録し、  頂点から出るときにも A 自分の親を記録する順序 B C D E F A,
  • 34.
    34 / 277  木の嬉しい順序(DFS順序) ◦ Euler Tour  頂点から入るときに記録し、  頂点から出るときにも A 自分の親を記録する順序 B C D E F A, B,
  • 35.
    35 / 277  木の嬉しい順序(DFS順序) ◦ Euler Tour  頂点から入るときに記録し、  頂点から出るときにも A 自分の親を記録する順序 B C D E F A, B, D,
  • 36.
    36 / 277  木の嬉しい順序(DFS順序) ◦ Euler Tour  頂点から入るときに記録し、  頂点から出るときにも A 自分の親を記録する順序 B C D E F A, B, D, B,
  • 37.
    37 / 277  木の嬉しい順序(DFS順序) ◦ Euler Tour  頂点から入るときに記録し、  頂点から出るときにも A 自分の親を記録する順序 B C D E F A, B, D, B, E
  • 38.
    38 / 277  木の嬉しい順序(DFS順序) ◦ Euler Tour  頂点から入るときに記録し、  頂点から出るときにも A 自分の親を記録する順序 B C D E F A, B, D, B, E, B,
  • 39.
    39 / 277  木の嬉しい順序(DFS順序) ◦ Euler Tour  頂点から入るときに記録し、  頂点から出るときにも A 自分の親を記録する順序 B C D E F A, B, D, B, E, B, A,
  • 40.
    40 / 277  木の嬉しい順序(DFS順序) ◦ Euler Tour  頂点から入るときに記録し、  頂点から出るときにも A 自分の親を記録する順序 B C D E F A, B, D, B, E, B, A, C,
  • 41.
    41 / 277  木の嬉しい順序(DFS順序) ◦ Euler Tour  頂点から入るときに記録し、  頂点から出るときにも A 自分の親を記録する順序 B C D E F A, B, D, B, E, B, A, C, F,
  • 42.
    42 / 277  木の嬉しい順序(DFS順序) ◦ Euler Tour  頂点から入るときに記録し、  頂点から出るときにも A 自分の親を記録する順序 B C D E F A, B, D, B, E, B, A, C, F, C,
  • 43.
    43 / 277  木の嬉しい順序(DFS順序) ◦ Euler Tour  頂点から入るときに記録し、  頂点から出るときにも A 自分の親を記録する順序 B C D E F A, B, D, B, E, B, A, C, F, C, A
  • 44.
    44 / 277  木の嬉しい順序(DFS順序) ◦ Euler Tour  木の辺を、行きと帰りの2つの辺から なるとみなすときの A オイラー閉路に対応する B C D E F A, B, D, B, E, B, A, C, F, C, A
  • 45.
    45 / 277  木の嬉しい順序(DFS順序) ◦ Euler Tour  木の辺を、行きと帰りの2つの辺から なるとみなすときの オイラー閉路に対応する  オイラー閉路 (Euler Tour) : 全ての辺を1度ずつ通る閉路  オイラー路はケーニヒスベルクの橋問題で有名 Wikipediaより。 CC3.0-BY-SA
  • 46.
    46 / 277  Euler TourによるLCAの計算 A B C D E F
  • 47.
    47 / 277  Euler TourによるLCAの計算 A B D B E B A C F C A 1 2 3 2 3 2 1 2 3 2 1 A B C D E F
  • 48.
    48 / 277  Euler TourによるLCAの計算 A B D B E B A C F C A 1 2 3 2 3 2 1 2 3 2 1 A B C D E F
  • 49.
    49 / 277  Euler TourによるLCAの計算 A B D B E B A C F C A 1 2 3 2 3 2 1 2 3 2 1 A B C D E F
  • 50.
    50 / 277  Euler TourによるLCAの計算 A B D B E B A C F C A 1 2 3 2 3 2 1 2 3 2 1 A B C D E F
  • 51.
    51 / 277  Euler TourによるLCAの計算 A B D B E B A C F C A 1 2 3 2 3 2 1 2 3 2 1 A 深さ最小 B C D E F
  • 52.
    52 / 277  Euler TourによるLCAの計算 A B D B E B A C F C A 1 2 3 2 3 2 1 2 3 2 1 A  RMQを利用 深さ最小 B C D E F
  • 53.
    53 / 277  正当性
  • 54.
    54 / 277  正当性: CがAとBのLCAのとき ◦ Euler Tour上でCは[A,B]に含まれる ◦ Euler Tour上で[A,B]に含まれるのはCの部分木  を言えばよい
  • 55.
    55 / 277  正当性(1): Euler Tour上でCは[A,B]に含まれる ◦ AからBに行くにはCを経由しないといけない(LCAの性質より) ◦ ので当たり前
  • 56.
    56 / 277  正当性(2): Euler Tour上で[A,B]に含まれるのは Cの部分木 ◦ Euler Tourにおいて部分木は連続した部分列として現れる ◦ ので当たり前
  • 57.
    57 / 277  LCAを求めて終わり?
  • 58.
    58 / 277  LCAを求めて終わり? ◦ あと少しだけやることがあります
  • 59.
    59 / 277  「削除がない場合の利点」の考察を思い出す
  • 60.
    60 / 277  「削除がない場合の利点」の考察を思い出す  LCAが存在するなら、最終的な木の上で計算すれば よい
  • 61.
    61 / 277  「削除がない場合の利点」の考察を思い出す  LCAが存在するなら、最終的な木の上で計算すれば よい
  • 62.
    62 / 277  A, Bが同じ木上にあるかどうかの判定が必要
  • 63.
    63 / 277  A, Bが同じ木上にあるかどうかの判定が必要 ですが
  • 64.
    64 / 277  A, Bが同じ木上にあるかどうかの判定が必要 ですが 辺の追加クエリしかないのでUnionFindでよい ということはすぐにわかると思います
  • 65.
    65 / 277  頂点数 𝑁 ≤ 106  クエリ数 𝑄 ≤ 106  辺の削除は行われない  𝑂(𝑁 + 𝑄 log 𝑁) なので間に合う
  • 66.
    66 / 277  ここまでの両方を実装すれば40点
  • 67.
    67 / 277  ここまでの両方を実装すれば40点  複数のアルゴリズムを条件によって使い分けるテクは さすがに使っていると思います
  • 68.
    68 / 277  頂点数 𝑁 ≤ 106  クエリ数 𝑄 ≤ 106
  • 69.
    69 / 277  頂点数 𝑁 ≤ 106  クエリ数 𝑄 ≤ 106  削除クエリもある
  • 70.
    70 / 277  追加も削除もある場合の頻出テク
  • 71.
    71 / 277  追加も削除もある場合の頻出テク ◦ クエリの(平方)分割 ◦ がんばって動的になんとかする
  • 72.
    72 / 277  追加も削除もある場合の頻出テク ◦ がんばって動的になんとかする
  • 73.
    73 / 277  追加も削除もある場合の頻出テク ◦ がんばって動的になんとかする
  • 74.
    74 / 277  木が静的な場合のLCA (復習)
  • 75.
    75 / 277  木が静的な場合のLCA (復習) ◦ Euler Tour上で必要とされるクエリは以下の通り
  • 76.
    76 / 277  木が静的な場合のLCA (復習) ◦ Euler Tour上で必要とされるクエリは以下の通り 1. 区間の最小値をとる
  • 77.
    77 / 277  木が静的な場合のLCA (復習) ◦ Euler Tour上で必要とされるクエリは以下の通り 1. 区間の最小値をとる ◦ RMQで実現可能
  • 78.
    78 / 277  木が動的な場合のLCA (絶望) ◦ Euler Tour上で必要とされるクエリは以下の通り 1. 区間の最小値をとる 2. ◦ RMQで実現可能な気がする
  • 79.
    79 / 277  木が動的な場合のLCA (絶望) ◦ Euler Tour上で必要とされるクエリは以下の通り 1. 区間の最小値をとる 2. 列の連結をする 3. ◦ RMQで実現可能な気がする
  • 80.
    80 / 277  木が動的な場合のLCA (絶望) ◦ Euler Tour上で必要とされるクエリは以下の通り 1. 区間の最小値をとる 2. 列の連結をする 3. 列の分割をする 4. ◦ RMQで実現可能な気がする
  • 81.
    81 / 277  木が動的な場合のLCA (絶望) ◦ Euler Tour上で必要とされるクエリは以下の通り 1. 区間の最小値をとる 2. 列の連結をする 3. 列の分割をする 4. それだけ?
  • 82.
    82 / 277  木が動的な場合のLCA (絶望) ◦ Euler Tour上で必要とされるクエリは以下の通り 1. 区間の最小値をとる 2. 列の連結をする 3. 列の分割をする 4. 区間に値を足す
  • 83.
    83 / 277  木が動的な場合のLCA (絶望) ◦ Euler Tour上で必要とされるクエリは以下の通り 1. 区間の最小値をとる 2. 列の連結をする 3. 列の分割をする 4. 区間に値を足す ◦ この業界では「Starry Sky木」として知られているもの
  • 84.
    84 / 277  木が動的な場合のLCA (絶望) ◦ Euler Tour上で必要とされるクエリは以下の通り 1. 区間の最小値をとる 2. 列の連結をする 3. 列の分割をする 4. 区間に値を足す ◦ この業界では「Starry Sky木」として知られているもの を平衡二分木として実装する必要がある (絶望)
  • 85.
    85 / 277  木が動的な場合のLCA (絶望) ◦ Euler Tour上で必要とされるクエリは以下の通り 1. 区間の最小値をとる 2. 列の連結をする 3. 列の分割をする 4. 区間に値を足す ◦ この業界では「Starry Sky木」として知られているもの を平衡二分木として実装する必要がある (絶望)  しかもmerge/splitベースで
  • 86.
    86 / 277  平衡二分木の中身は後回し
  • 87.
    87 / 277  平衡二分木の中身は後回し  平衡二分木を使った具体的な実装方法
  • 88.
    88 / 277  木の各頂点ごとに、Euler Tourのためのノードを2つ 用意する(𝑆 𝐴 , 𝐺 𝐴 ) ◦ 𝑆 𝐴 上には頂点Aの番号と、その深さが記録されている ◦ 𝐺 𝐴 上には頂点Aの親Pの番号と、その深さが記録されている ◦ Aが根のときは𝑆 𝐴 のみ使う A A 𝐺𝐵 𝑆𝐴 B B
  • 89.
    89 / 277  木の連結 A Depth 0 B C Depth 0 Depth 1 D E Depth 1 F Depth 1 𝑆𝐵 𝐵 , 𝑆𝐷 𝐷 , 𝐺 𝐷 𝐵 , 𝑆 𝐸 𝐸 , 𝐺 𝐸 (𝐵) Depth 2 𝑆 𝐴 𝐴 , 𝑆 𝐶 𝐶 , 𝑆 𝐹 𝐹 , 𝐺 𝐹 𝐶 , 𝐺 𝐹 (𝐴)
  • 90.
    90 / 277  木の連結 A 1. Euler TourをA点で分割 Depth 0 ◦ A直下ならどの位置でもいい  𝑆 𝐴 の直後がおすすめ B C Depth 0 Depth 1 D E Depth 1 F Depth 1 𝑆𝐵 𝐵 , 𝑆𝐷 𝐷 , 𝐺 𝐷 𝐵 , 𝑆 𝐸 𝐸 , 𝐺 𝐸 (𝐵) Depth 2 𝑆 𝐴 𝐴 ,                            𝑆 𝐶 𝐶 , 𝑆 𝐹 𝐹 , 𝐺 𝐹 𝐶 , 𝐺 𝐹 (𝐴)
  • 91.
    91 / 277  木の連結 A 1. Euler TourをA点で分割 Depth 0 2. Euler Tourを挿入 B C Depth 0 Depth 1 D E F Depth 1 Depth 1 Depth 2 𝑆 𝐴 𝐴 , 𝑆 𝐵 𝐵 , 𝑆 𝐷 𝐷 , 𝐺 𝐷 𝐵 , 𝑆 𝐸 𝐸 , 𝐺 𝐸 𝐵 , 𝐺 𝐵 (𝐸), 𝑆 𝐶 𝐶 , 𝑆 𝐹 𝐹 , 𝐺 𝐹 𝐶 , 𝐺 𝐹 (𝐴)
  • 92.
    92 / 277  木の連結 A 1. Euler TourをA点で分割 Depth 0 2. Euler Tourを挿入 3. 深さを調整 B C Depth 1 Depth 1 D E F Depth 2 Depth 2 Depth 2 𝑆 𝐴 𝐴 , 𝑆 𝐵 𝐵 , 𝑆 𝐷 𝐷 , 𝐺 𝐷 𝐵 , 𝑆 𝐸 𝐸 , 𝐺 𝐸 𝐵 , 𝐺 𝐵 (𝐸), 𝑆 𝐶 𝐶 , 𝑆 𝐹 𝐹 , 𝐺 𝐹 𝐶 , 𝐺 𝐹 (𝐴)
  • 93.
    93 / 277  木の削除: 追加のときと逆操作
  • 94.
    94 / 277  平衡Starry Sky Treeがあればよいことがわかった
  • 95.
    95 / 277  平衡Starry Sky Treeがあればよいことがわかった  ではどのように実装するか?(実装例)
  • 96.
    96 / 277  今回は、葉ノードと内部ノードの区別をしない ◦ A,B,C,D,Eはどれも列上の項目とする D B E A C
  • 97.
    97 / 277  各ノードは、Δ𝑥 𝐴 というフィールドを持つ D B E A C
  • 98.
    98 / 277  各ノードは、Δ𝑥 𝐴 というフィールドを持つ  各ノードに定めたい値𝑥 𝐴 は、 𝑥𝐴 = Δ𝑥 𝐴 D 𝐴から根までのパス上のノード B E A C
  • 99.
    99 / 277  𝑥 𝐴 = Δ𝑥 𝐷 + Δ𝑥 𝐵 + Δ𝑥 𝐴  𝑥 𝐵 = Δ𝑥 𝐷 + Δ𝑥 𝐵  𝑥 𝐶 = Δ𝑥 𝐷 + Δ𝑥 𝐵 + Δ𝑥 𝐶  𝑥 𝐷 = Δ𝑥 𝐷 D  𝑥 𝐸 = Δ𝑥 𝐷 + Δ𝑥 𝐸 B E A C
  • 100.
    100 / 277  木の回転: 𝑥 𝐴 が保存されるように行う  Δ𝑥 ′𝐴 = 𝑥𝐴 − 𝑥𝐵  Δ𝑥 ′𝐵 = 𝑥𝐵 B  Δ𝑥 ′𝐶 = 𝑥𝐶 − 𝑥𝐷  Δ𝑥 ′𝐷 = 𝑥𝐷− 𝑥𝐵  Δ𝑥 ′𝐸 = 𝑥𝐸− 𝑥𝐷 A D C E
  • 101.
    101 / 277  木の回転: 𝑥 𝐴 が保存されるように行う  Δ𝑥 ′𝐴 = Δ𝑥 𝐴  Δ𝑥 ′𝐵 = Δ𝑥 𝐷 + Δ𝑥 𝐵 B  Δ𝑥 ′𝐶 = Δ𝑥 𝐵 + Δ𝑥 𝐶  Δ𝑥 ′𝐷 = −Δ𝑥 𝐵  Δ𝑥 ′𝐸 = Δ𝑥 𝐸 A D C E
  • 102.
    102 / 277  最後に、ノードに値𝑦 𝐴 を  𝑦𝐴 = min 𝑥𝐵 − 𝑥𝐴 𝐴の全ての子孫 𝐵  となるように計算して保持しておく
  • 103.
    103 / 277  最後に、ノードに値𝑦 𝐴 を  𝑦𝐴 = min 𝑥𝐵 − 𝑥𝐴 𝐴の全ての子孫 𝐵  となるように計算して保持しておく  これでStarry Sky Tree相当の計算を行えるようになる
  • 104.
    104 / 277  平衡二分木の基本
  • 105.
    105 / 277  平衡二分木の基本:回転操作
  • 106.
    106 / 277  回転操作: 順序を保存したまま木構造を変形
  • 107.
    107 / 277  回転操作: 順序を保存したまま木構造を変形  次のような二分木を考える D B E A C
  • 108.
    108 / 277  回転操作: 順序を保存したまま木構造を変形  次のような二分木を考える  順序: 左の子孫→自分→右の子孫 D B E A C
  • 109.
    109 / 277  回転操作: 順序を保存したまま木構造を変形  次のような二分木を考える  順序: 左の子孫→自分→右の子孫 D  この場合は A, B, C, D, E の順番 B E A C
  • 110.
    110 / 277  次のように変形しても順番はA,B,C,D,E D B B E A D A C C E
  • 111.
    111 / 277  次のように変形しても順番はA,B,C,D,E  これを「木の回転」と言う D B B E A D A C C E
  • 112.
    112 / 277  次のように変形しても順番はA,B,C,D,E  これを「木の回転」と言う  うまく回転をすることで、偏りが起きないようにする二 分木を「平衡二分木」と言う ◦ 回転以外の方法で平衡を保つものもある
  • 113.
    113 / 277  平衡二分木の実装方法
  • 114.
    114 / 277  平衡二分木の実装方法  今回は何でもOK! ◦ 赤黒木 ◦ RBST ◦ Treap ◦ Splay木 ◦ などなど…
  • 115.
    115 / 277  この解説ではSplay木の説明をします
  • 116.
    116 / 277  突然ですが、Union Findの復習をします
  • 117.
    117 / 277  突然ですが、Union Findの復習をします A  Union Find の効率化テクニック:  アクセスした頂点を根へ持っていく B F C D E
  • 118.
    118 / 277  突然ですが、Union Findの復習をします A  Union Find の効率化テクニック:  アクセスした頂点を根へ持っていく B F C D E
  • 119.
    119 / 277  突然ですが、Union Findの復習をします A  Union Find の効率化テクニック:  アクセスした頂点を根へ持っていく B F C D E
  • 120.
    120 / 277  突然ですが、Union Findの復習をします A  Union Find の効率化テクニック:  アクセスした頂点を根へ持っていく B F C D E
  • 121.
    121 / 277  突然ですが、Union Findの復習をします A  Union Find の効率化テクニック:  アクセスした頂点を根へ持っていく B F C D E
  • 122.
    122 / 277  突然ですが、Union Findの復習をします A  Union Find の効率化テクニック:  アクセスした頂点を根へ持っていく B F C D E
  • 123.
    123 / 277  同じようなことを、二分探索木でもできないか?
  • 124.
    124 / 277  同じようなことを、二分探索木でもできないか? ◦ →Move-to-root heuristic
  • 125.
    125 / 277  Move-to-root heuristic ◦ 頂点にアクセスしたら、それが根に行くまで繰り返し回転する
  • 126.
    126 / 277  Move-to-root heuristic ◦ 頂点にアクセスしたら、それが根に行くまで繰り返し回転する ◦ そんなので上手くいくわけないだろ!!
  • 127.
    127 / 277  実際ダメ
  • 128.
    128 / 277  実際ダメ E D C B A
  • 129.
    129 / 277  実際ダメ E D C B A
  • 130.
    130 / 277  実際ダメ E D C A B
  • 131.
    131 / 277  実際ダメ E D A C B
  • 132.
    132 / 277  実際ダメ E A D C B
  • 133.
    133 / 277  実際ダメ A E D C B
  • 134.
    134 / 277  実際ダメ  この後A, B, C, D, Eの順にアクセスしたら 𝑛 + 𝑛 − 1 + … + 1 = 𝑂 𝑛2  のコストがかかってしまう
  • 135.
    135 / 277  実際ダメ  この後A, B, C, D, Eの順にアクセスしたら 𝑛 + 𝑛 − 1 + … + 1 = 𝑂 𝑛2  のコストがかかってしまう  どうする?
  • 136.
    136 / 277  解決策: 木の回転を3つに分ける
  • 137.
    137 / 277  解決策: 木の回転を3つに分ける ◦ “zig” step ◦ “zig-zag” step ◦ “zig-zig” step
  • 138.
    138 / 277  (1) “zig”-step
  • 139.
    139 / 277  (1) “zig”-step  すぐ上が根の場合 R A
  • 140.
    140 / 277  (1) “zig”-step  すぐ上が根の場合  普通に回転する A R
  • 141.
    141 / 277  (2) “zig-zag”-step
  • 142.
    142 / 277  (2) “zig-zag”-step  左→右、またはその逆のとき G P A
  • 143.
    143 / 277  (2) “zig-zag”-step  左→右、またはその逆のとき G  普通に2回回転する A P
  • 144.
    144 / 277  (2) “zig-zag”-step  左→右、またはその逆のとき  普通に2回回転する A P G
  • 145.
    145 / 277  (2) “zig-zag”-step  左→右、またはその逆のとき  普通に2回回転する  ここまでは先ほどと同じ
  • 146.
    146 / 277  (3) “zig-zig”-step
  • 147.
    147 / 277  (3) “zig-zig”-step  左→左、またはその逆のとき G P A
  • 148.
    148 / 277  (3) “zig-zig”-step  左→左、またはその逆のとき G  2回ではなく3回回転する P A
  • 149.
    149 / 277  (3) “zig-zig”-step  左→左、またはその逆のとき G  2回ではなく3回回転する A P
  • 150.
    150 / 277  (3) “zig-zig”-step  左→左、またはその逆のとき A  2回ではなく3回回転する G P
  • 151.
    151 / 277  (3) “zig-zig”-step  左→左、またはその逆のとき A  2回ではなく3回回転する P G  (ただし、後でこれを2回として扱う)
  • 152.
    152 / 277  Splaying operation ◦ 偏った位置にあるときだけ余計に回転する
  • 153.
    153 / 277  Splaying operation ◦ 偏った位置にあるときだけ余計に回転する ◦ そんなので上手くいくわけないだろ!!
  • 154.
    154 / 277  実は上手くいく
  • 155.
    155 / 277  実は上手くいく  具体的には: 𝑂(log 𝑁) amortized
  • 156.
    156 / 277  実は上手くいく  具体的には: 𝑂(log 𝑁) amortized
  • 157.
    157 / 277  ならし計算量 (amortized time complexity)  N個の一連の操作が𝑂(𝑓(𝑁))で行えるとする 𝑓 𝑁  1つ1つの操作は、本当は𝑂( )とは限らない 𝑁 𝑓 𝑁  これを𝑂( )として扱うのが、ならし計算量 𝑁
  • 158.
    158 / 277  ならし計算量のイメージ 操作1 本当の計算量 操作2 操作3 操作4 ならし計算量 操作5
  • 159.
    159 / 277  ならし計算量の向き/不向き
  • 160.
    160 / 277  ならし計算量の向き/不向き  向いているもの ◦ 全体での処理効率が重視されるバッチ型の処理 ◦ 例: プログラミングコンテスト  向いていないもの ◦ リアルタイム性能が重視される処理 ◦ 例: 信号処理
  • 161.
    161 / 277  ならし計算量と平均計算量
  • 162.
    162 / 277  ならし計算量と平均計算量 ◦ この2つは別物!! ◦ ならし計算量 : 時系列上での平均 ◦ 平均計算量 : 確率変数上での平均
  • 163.
    163 / 277  ならし計算量と平均計算量 ◦ この2つは別物!! ◦ ならし計算量 : 時系列上での平均 ◦ 平均計算量 : 確率変数上での平均 ◦ ならし計算量 : 不確定要素は無い!
  • 164.
    164 / 277  Splayのならし計算量の評価
  • 165.
    165 / 277  Splayのならし計算量の評価  「ポテンシャル関数」の概念を導入
  • 166.
    166 / 277  Splayのならし計算量の評価  「ポテンシャル関数」の概念を導入 ◦ 借金みたいなもの
  • 167.
    167 / 277  ポテンシャル関数を用いた計算量の均し(ならし)  𝑎 𝑗 = 𝑡 𝑗 + Φ 𝑗+1 − Φj ◦ 𝑡 𝑗 : その操作の実際の計算量 ◦ Φ 𝑗+1 − Φj : ポテンシャルの増加量 ◦ 𝑎 𝑗 : その操作のならし計算量
  • 168.
    168 / 277  ポテンシャル関数を用いた計算量の均し(ならし)  𝑎 𝑗 = 𝑡 𝑗 + Φ 𝑗+1 − Φj ◦ 𝑡 𝑗 : その操作の実際の計算量 ◦ Φ 𝑗+1 − Φj : ポテンシャルの増加量 ◦ 𝑎 𝑗 : その操作のならし計算量  ポテンシャルの意味 ◦ より大きい: 木はより偏っている ◦ より小さい: 木はより平坦になっている
  • 169.
    169 / 277  ならし計算量の総和をとる  𝑗 𝑎𝑗 = 𝑗 𝑡 𝑗 + Φ 𝑚 − Φ0 ◦ 𝑗 𝑡 𝑗 : 実際の計算量の総和 ◦ Φ 𝑚 − Φ0 : ポテンシャルの総変化量 ◦ 𝑗 𝑎 𝑗 : ならし計算量の総和
  • 170.
    170 / 277  ならし計算量の総和をとる  𝑗 𝑎𝑗 = 𝑗 𝑡 𝑗 + Φ 𝑚 − Φ0 ◦ 𝑗 𝑡 𝑗 : 実際の計算量の総和 ◦ Φ 𝑚 − Φ0 : ポテンシャルの総変化量 ◦ 𝑗 𝑎 𝑗 : ならし計算量の総和  ポテンシャルの総変化量が小さければうまく評価でき る
  • 171.
    171 / 277  Splay木のポテンシャル ◦ Splay木の各頂点の重さを𝑤(𝑥)とする  計算量の見積もり方にあわせて自由に決めてよい ◦ Splay木の頂点のサイズ s 𝑥 = 𝑤(𝑦) 𝑥の全ての子孫 𝑦 ◦ Splay木の頂点のランク 𝑟 𝑥 = log 2 𝑠(𝑥) ◦ Splay木のポテンシャル Φ = 𝑟(𝑥) 全ての頂点 𝑥
  • 172.
    172 / 277  Splay木のポテンシャル: 例
  • 173.
    173 / 277  Splay木のポテンシャル: 例  重さ 𝑤(𝑥) 1 (今回は全て1とする) 1 1 1 1 1 1
  • 174.
    174 / 277  Splay木のポテンシャル: 例  サイズ 𝑠(𝑥) 7 ◦ 部分木の重さの和 5 1 1 3 1 1
  • 175.
    175 / 277  Splay木のポテンシャル: 例  ランク 𝑟 𝑥 = log 2 𝑠(𝑥) 2.8 2.3 0.0 0.0 1.6 0.0 0.0
  • 176.
    176 / 277  Splay木のポテンシャル: 例  ポテンシャル: ランクの総和  Φ = 2.8 + 2.3 + 1.6 = 6.7
  • 177.
    177 / 277  Splay木のポテンシャルの良い性質
  • 178.
    178 / 277  Splay木のポテンシャルの良い性質 …  回転の影響を受ける頂点が少ない ◦ 解析が簡単になる
  • 179.
    179 / 277  アクセス補題 (Access Lemma)
  • 180.
    180 / 277  アクセス補題 (Access Lemma)  𝑥 : 木のノード  𝑡 : 木の根 とするとき  木をsplayする操作一回にかかる時間(回転の回数) は、ならし計算量で 3𝑟 𝑡 − 3𝑟 𝑥 + 1  以下である。
  • 181.
    181 / 277  アクセス補題の証明
  • 182.
    182 / 277  アクセス補題の証明  各回転ステップのならし計算量が 1. “zig”-stepでは 3𝑟 ′ 𝑥 − 3𝑟 𝑥 + 1 以下 2. それ以外では 3𝑟 ′ 𝑥 − 3𝑟(𝑥) 以下  (ただし、𝑟′(𝑥) : 操作後のランク)  であることを示す。  そうすると、1のケースに登場する𝑟′(𝑥)は初期の𝑟(𝑡) と等しい(木全体のサイズの対数)ので、合計すると 3𝑟 𝑡 − 3𝑟 𝑥 + 1になる。
  • 183.
    183 / 277  アクセス補題の証明 (1) “zig”-step の場合  𝑟 𝑋 = log 2 (𝑤 𝑋 + 𝑤 𝐴 + 𝑤 𝐵 ) R  𝑟 𝑅 = log 2 (𝑤 𝑋 + 𝑤 𝐴 + 𝑤 𝐵 + 𝑤 𝐶 + 𝑤(𝑅)) X C A B
  • 184.
    184 / 277  アクセス補題の証明 (1) “zig”-step の場合  𝑟 𝑋 = log 2 (𝑤 𝑋 + 𝑤 𝐴 + 𝑤 𝐵 )X  𝑟 ′ 𝑋 = log 2 (𝑤 𝑋 + 𝑤 𝐴 + 𝑤 𝐵 + 𝑤 𝐶 + 𝑤 𝑅 )  𝑟 𝑅 = log 2 (𝑤 𝑋 + 𝑤 𝐴 + 𝑤 𝐵 + 𝑤 𝐶 + 𝑤(𝑅))  𝑟 ′ 𝑅 = log 2 (𝑤 𝑅 + 𝑤 𝐵 + A 𝐶 ) R 𝑤 B C
  • 185.
    185 / 277  アクセス補題の証明 (1) “zig”-step の場合  𝑟 𝑋 = log 2 (𝑤 𝑋 + 𝑤 𝐴 + 𝑤 𝐵 )  𝑟 ′ 𝑋 = log 2 (𝑤 𝑋 + 𝑤 𝐴 + 𝑤 𝐵 + 𝑤 𝐶 + 𝑤 𝑅 )  𝑟 𝑅 = log 2 (𝑤 𝑋 + 𝑤 𝐴 + 𝑤 𝐵 + 𝑤 𝐶 + 𝑤(𝑅))  𝑟 ′ 𝑅 = log 2 (𝑤 𝑅 + 𝑤 𝐵 + 𝑤 𝐶 )
  • 186.
    186 / 277  アクセス補題の証明 (1) “zig”-step の場合  𝑟 𝑋 = log 2 (𝑤 𝑋 + 𝑤 𝐴 + 𝑤 𝐵 )  𝑟 ′ 𝑋 = log 2 (𝑤 𝑋 + 𝑤 𝐴 + 𝑤 𝐵 + 𝑤 𝐶 + 𝑤 𝑅 )  𝑟 𝑅 = log 2 (𝑤 𝑋 + 𝑤 𝐴 + 𝑤 𝐵 + 𝑤 𝐶 + 𝑤(𝑅))  𝑟 ′ 𝑅 = log 2 (𝑤 𝑅 + 𝑤 𝐵 + 𝑤 𝐶 )  𝑟 𝑋 ≤ 𝑟 ′ 𝑋 , 𝑟 ′ 𝑅 ≤ 𝑟(𝑅)
  • 187.
    187 / 277  アクセス補題の証明 (1) “zig”-step の場合  𝑟 𝑋 ≤ 𝑟 ′ 𝑋 , 𝑟 ′ 𝑅 ≤ 𝑟(𝑅)  ならし計算量 𝑎 = 𝑡 + Φ′ − Φ = 1 + 𝑟′ 𝑋 + 𝑟′ 𝑅 − 𝑟 𝑋 − 𝑟 𝑅 ≤ 1 + 𝑟′ 𝑋 − 𝑟 𝑋 ≤ 1 + 3𝑟 ′ 𝑋 − 3𝑟 𝑋
  • 188.
    188 / 277  アクセス補題の証明 (2) “zigzig”-step の場合  𝑟 𝑋 = log 2 (𝑤 𝑋 + 𝑠 𝐴 + 𝑠 𝐵 ) G  D 𝑟 𝑃 = log 2 (𝑤 𝑋 + 𝑠 𝐴 + 𝑠 𝐵 + 𝑠 𝐶P + 𝑤(𝑃))  𝑟 𝐺 = X C log 2 (𝑤 𝑋 + 𝑠 𝐴 + 𝑠 𝐵 + 𝑠 𝐶 + 𝑤 𝑃 + 𝑠 𝐷 + 𝑤 𝐺 ) A B
  • 189.
    189 / 277  アクセス補題の証明 (2) “zigzig”-step の場合  𝑟 𝑋 = log 2 (𝑤 𝑋 + 𝑠 𝐴 + 𝑠 𝐵 ) X ′ 𝑋 =  𝑟 log 2 (𝑤 𝑋 + 𝑠 𝐴 + 𝑠 𝐵 + 𝑠 𝐶 + 𝑠 𝐷 + 𝑤 𝑃 + 𝑤 𝐺 ) A P  𝑟 𝑃 = log 2 (𝑤 𝑋 + 𝑠 𝐴 + 𝑠 𝐵 + 𝑠 𝐶 + 𝑤(𝑃))  𝑟 ′ 𝑃 = log 2 (𝑠 𝐵 + 𝑠 𝐶 + 𝑤 𝑃 + 𝑤 𝐺 ) B G  𝑟 𝐺 = log 2 (𝑤 𝑋 + 𝑠 𝐴 + 𝑠 𝐵 + 𝑠 𝐶 + 𝑤 𝑃 + 𝑠 𝐷 + 𝑤 𝐺 )  𝑟 ′ 𝐺 = log 2 (𝑠 𝐶 + 𝑠 𝐷 + 𝑤 𝐺 ) C D
  • 190.
    190 / 277  アクセス補題の証明 (2) “zigzig”-step の場合  𝑟 𝑋 = log 2 (𝑤 𝑋 + 𝑠 𝐴 + 𝑠 𝐵 )  𝑟′ 𝑋 = log 2 (𝑤 𝑋 + 𝑠 𝐴 + 𝑠 𝐵 + 𝑠 𝐶 + 𝑠 𝐷 + 𝑤 𝑃 + 𝑤 𝐺 )  𝑟 𝑃 = log 2 (𝑤 𝑋 + 𝑠 𝐴 + 𝑠 𝐵 + 𝑠 𝐶 + 𝑤(𝑃))  𝑟 ′ 𝑃 = log 2 (𝑠 𝐵 + 𝑠 𝐶 + 𝑤 𝑃 + 𝑤 𝐺 )  𝑟 𝐺 = log 2 (𝑤 𝑋 + 𝑠 𝐴 + 𝑠 𝐵 + 𝑠 𝐶 + 𝑤 𝑃 + 𝑠 𝐷 + 𝑤 𝐺 )  𝑟 ′ 𝐺 = log 2 (𝑠 𝐶 + 𝑠 𝐷 + 𝑤 𝐺 )
  • 191.
    191 / 277  アクセス補題の証明 (2) “zigzig”-step の場合  𝑟′ 𝑋 = 𝑟 𝐺 , 𝑟′ 𝑋 ≤ 𝑟′ 𝑃 , 𝑟 𝑃 ≤ 𝑟 𝑋
  • 192.
    192 / 277  アクセス補題の証明 (2) “zigzig”-step の場合  𝑟′ 𝑋 = 𝑟 𝐺 , 𝑟′ 𝑃 ≤ 𝑟′ 𝑋 , 𝑟 𝑋 ≤ 𝑟 𝑃 𝑎 = 𝑡 + Φ′ − Φ = 2 + 𝑟′ 𝑋 + 𝑟′ 𝑃 + 𝑟′ 𝐺 − 𝑟 𝑋 − 𝑟 𝑃 − 𝑟 𝐺 ≤ 2 + 𝑟 ′ 𝑋 + 𝑟 ′ 𝐺 − 2𝑟 𝑋
  • 193.
    193 / 277  アクセス補題の証明 (2) “zigzig”-step の場合  2 + 𝑟 ′ 𝑋 + 𝑟 ′ 𝐺 − 2𝑟 𝑋 ≤ 3𝑟 ′ 𝑋 − 3𝑟 𝑋  理由: 𝑟 ′ 𝐺 + 𝑟 𝑋 − 2𝑟 ′ 𝑋 ≤ −2 を示したい。 𝑠′ 𝐺 𝑠 𝑋  ところで左辺はlog 2 + log 2 であり、 𝑠′ 𝑋 𝑠′ 𝑋  log 2 𝑥が上に凸で、𝑠 ′ 𝐺 + 𝑠 𝑋 ≤ 𝑠′(𝑋)なのでこの 値は高々−2  よって不等式は示された。
  • 194.
    194 / 277  アクセス補題の証明 (3) “zigzag”-step の場合  𝑟′ 𝑋 = 𝑟 𝐺 , 𝑟 𝑋 ≤ 𝑟 𝑃  𝑠′ 𝑃 + 𝑠′ 𝐺 ≤ 𝑠′ 𝑋 G X P D P G A X A B C D B C
  • 195.
    195 / 277  アクセス補題の証明 (3) “zigzag”-step の場合  以下(2)と同様
  • 196.
    196 / 277  以上より、Splay操作がならし計算量で𝑂(log 𝑁)であ ることがわかった。  ところで、Splay操作のポテンシャルは高々 𝑂(𝑁 log 𝑁 )なので、全体で𝑂((𝑄 + 𝑁) log 𝑁)でクエリ を処理できることがわかった。
  • 197.
    197 / 277  以上より、Splay操作がならし計算量で𝑂(log 𝑁)であ ることがわかった。  ところで、Splay操作のポテンシャルは高々 𝑂(𝑁 log 𝑁 )なので、全体で𝑂((𝑄 + 𝑁) log 𝑁)でクエリ を処理できることがわかった。  以上、満点解法その1
  • 198.
    198 / 277  満点解法その2 - Link/Cut木
  • 199.
    199 / 277  満点解法その2 - Link/Cut木  Link/Cut木 ◦ 元々、フローアルゴリズムの高速化のためにSleatorとTarjan が考案したもの
  • 200.
    200 / 277  満点解法その2 - Link/Cut木  Link/Cut木 ◦ 元々、フローアルゴリズムの高速化のためにSleatorとTarjan が考案したもの ◦ この問題のために必要な実装は、それよりもはるかに容易
  • 201.
    201 / 277  満点解法その2 - Link/Cut木  Link/Cut木 ◦ 元々、フローアルゴリズムの高速化のためにSleatorとTarjan が考案したもの ◦ この問題のために必要な実装は、それよりもはるかに容易 →Link/Cut木の練習としても適している
  • 202.
    202 / 277  満点解法その2 - Link/Cut木  Link/Cut木 ◦ 元々、フローアルゴリズムの高速化のためにSleatorとTarjan が考案したもの ◦ この問題のために必要な実装は、それよりもはるかに容易 →Link/Cut木の練習としても適している ◦ いろいろなバージョンがあるが、Splay木によるものが使いや すい
  • 203.
    203 / 277  予備知識 – Heavy/Light decomposition
  • 204.
    204 / 277  予備知識 – Heavy/Light decomposition ◦ 木をパスに分割する方法 A B C D E F
  • 205.
    205 / 277  予備知識 – Heavy/Light decomposition ◦ 木をパスに分割する方法 A B C D E F
  • 206.
    206 / 277  予備知識 – Heavy/Light decomposition ◦ 木をパスに分割する方法  変な形の木でも、「パスの木」の形に潰すと安定する
  • 207.
    207 / 277  Splay Treeの世界 列データ Splay Tree 列の畳み込みを効率よく計算
  • 208.
    208 / 277  Splay Treeの世界 列データ Splay Tree 列の畳み込みを効率よく計算
  • 209.
    209 / 277  Heavy/Light decompositionの世界 有向木 列データ Splay Tree 有向木を分解したもの 列の畳み込みを効率よく計算
  • 210.
    210 / 277  Link/Cut Treeの世界 有向木 Link/Cut Tree パスの畳み込みを効率よく計算 列データ Splay Tree 有向木を分解したもの 列の畳み込みを効率よく計算
  • 211.
    211 / 277  Link/Cut Treeの世界  H/L分解 = パスからなる木
  • 212.
    212 / 277  Link/Cut Treeの世界  H/L分解 = パスからなる木  Link/Cut Tree = Splay木からなる木
  • 213.
    213 / 277  Link/Cut Tree の辺は二種類ある ◦ Solid(Heavy) edge ◦ Dashed(Light) edge
  • 214.
    214 / 277  Link/Cut Tree の辺は二種類ある Solid(Heavy) Dashed(Light) 所属 Splay Tree H/L分解の木 分類 二分木 多分木 左右の区別 左右の区別あり なし 親 本当は祖先か子孫 本当の親 子供 本当は祖先か子孫 本当は子孫
  • 215.
    215 / 277  Solid, Dashedの区別
  • 216.
    216 / 277  Solid, Dashedの区別  いずれも、親方向リンクを持つ X L R M
  • 217.
    217 / 277  Solid, Dashedの区別  いずれも、親方向リンクを持つ  親から左方向リンクがあれば、Solid X L R M
  • 218.
    218 / 277  Solid, Dashedの区別  いずれも、親方向リンクを持つ  親から左方向リンクがあれば、Solid  親から右方向リンクがあれば、Solid X L R M
  • 219.
    219 / 277  Solid, Dashedの区別  いずれも、親方向リンクを持つ  親から左方向リンクがあれば、Solid  親から右方向リンクがあれば、Solid X  どちらもなければ、Dashed L R M
  • 220.
    220 / 277  Solid, Dashedの区別  いずれも、親方向リンクを持つ  親から左方向リンクがあれば、Solid  親から右方向リンクがあれば、Solid  どちらもなければ、Dashed  「右, 左, 親」の3つのリンクだけで構造を保持できる!
  • 221.
    221 / 277  小さな例 A B C F D E
  • 222.
    222 / 277  小さな例 A B C F D E
  • 223.
    223 / 277  小さな例 F B A A B D C C F D E E
  • 224.
    224 / 277  小さな例 B A B F A C F D E D F B A C D C E E
  • 225.
    225 / 277  Link/Cut Treeの操作: splayLC()  Link/Cut Tree上では、任意の頂点Xを根に持っていく ことができる ◦ 元の木の構造は変化しないことに注意
  • 226.
    226 / 277  前準備: splay  各Splay Tree上でsplayをすることで、Xから今の根ま でを点線だけで行けるようにする
  • 227.
    227 / 277  前準備: splay B  各Splay Tree上でsplayをすることで、Xから今の根ま F A でを点線だけで行けるようにする D C E
  • 228.
    228 / 277  前準備: splay B  各Splay Tree上でsplayをすることで、Xから今の根ま F A でを点線だけで行けるようにする C D E
  • 229.
    229 / 277  前準備: splay B  各Splay Tree上でsplayをすることで、Xから今の根ま F A でを点線だけで行けるようにする C EからBまで、点 線のみで行ける D ようになる E
  • 230.
    230 / 277  splayLC() のメイン操作: つなぎ変え(expose操作)  パスのつなぎ変え操作を行う  元の木ではこんな感じ
  • 231.
    231 / 277  A splayLC() のメイン操作: つなぎ変え(expose操作)  B パスのつなぎ変え操作を行う  元の木ではこんな感じ C F D E
  • 232.
    232 / 277  A splayLC() のメイン操作: つなぎ変え(expose操作)  B パスのつなぎ変え操作を行う  元の木ではこんな感じ C F D E
  • 233.
    233 / 277  A splayLC() のメイン操作: つなぎ変え(expose操作)  B パスのつなぎ変え操作を行う  元の木ではこんな感じ C F D E
  • 234.
    234 / 277  splayLC() のメイン操作: つなぎ変え(expose操作)  パスのつなぎ変え操作を行う  Link/Cut 木でも、左向きのSolid辺を付け替えるだけ
  • 235.
    235 / 277  splayLC() のメイン操作: B つなぎ変え(expose操作)  F パスのつなぎ変え操作を行う A  Link/Cut 木でも、左向きのSolid辺を付け替えるだけ C D E
  • 236.
    236 / 277  splayLC() のメイン操作: B つなぎ変え(expose操作)  F パスのつなぎ変え操作を行う A  Link/Cut 木でも、左向きのSolid辺を付け替えるだけ C D E
  • 237.
    237 / 277 B  splayLC() のメイン操作: つなぎ変え(expose操作)  C パスのつなぎ変え操作を行う A  E Link/Cut 木でも、左向きのSolid辺を付け替えるだけ D F
  • 238.
    238 / 277  左向きの辺をつなぎ替えるだけで、Eが一番上の木に 所属するようになった
  • 239.
    239 / 277  左向きの辺をつなぎ替えるだけで、Eが一番上の木に 所属するようになった  最後にもう1度splay()操作を行うことで、Link/Cut Treeの一番上の根にEが来る
  • 240.
    240 / 277  L/C木のsplayLC()はSplay Treeの解析を少し応用す ると、対数時間であることが言える
  • 241.
    241 / 277  L/C木のsplayLC()はSplay Treeの解析を少し応用す ると、対数時間であることが言える  1回ごとのsplay()操作が対数時間であることは既に わかっている
  • 242.
    242 / 277  L/C木のsplayLC()はSplay Treeの解析を少し応用す ると、対数時間であることが言える  1回ごとのsplay()操作が対数時間であることは既に わかっている  しかし実際にはsplay()操作が𝑘回呼ばれている ◦ 𝑘は、パス分割された木の上での深さ
  • 243.
    243 / 277  ポテンシャルの定義 ◦ サイズ = solid/dashedに関わらず、子孫になっている頂点の 数 ◦ ランク = その対数 ◦ ポテンシャル = ランクの総和の2倍 として定める
  • 244.
    244 / 277  Splay Treeのならし計算量は 1 + 3𝑟 𝑡 − 3𝑟(𝑥) だっ た  今回のならし計算量は𝑘 + 6𝑟 𝑡 − 6𝑟(𝑥)になる ◦ 「Splayが𝑘回呼ばれる」という認識を改めてみる ◦ Splayは根に向かって順番に呼ばれるということを考慮すると、 「Splayが1回呼ばれるが、途中でk回、強制的にzigステップを 使われるかもしれない」と考えることができる ◦ 係数が2倍なのはポテンシャルの定義を変えたから
  • 245.
    245 / 277  余った定数項𝑘の回収  Expose操作のあとに1回行うsplay操作: k回の回転を 行う。  ポテンシャルの定義を2倍にしたので、splayの回転操 作1回につき1の追加コストを課しても問題ない
  • 246.
    246 / 277  ならしコスト6 log 2 𝑁のsplay操作を2回呼んでいるの で、splayLC()のならし計算量は12 log 2 𝑁 = 𝑂(log 𝑁) であるとわかった。
  • 247.
    247 / 277  AとBのLCAを求める。
  • 248.
    248 / 277  AとBのLCAを求めるには、まず 1. Bに対してsplayLC()を行う 2. Aに対してsplayLC()を行う  このとき、Bは浅い位置にいる。 ◦ Splay Treeに対するSplay操作1回で、他の頂点の深さは高々 2段しか下がらないので、この時点でBは深さ高々4程度。
  • 249.
    249 / 277  AとBの位置関係に基いて条件分岐
  • 250.
    250 / 277  AとBの位置関係に基いて条件分岐  (1) BがAの左側にある場合 ◦ この場合は、BはAの子孫ということになるので、AとBのLCA はAになる。  (2) BがAの右側にある場合 ◦ 次のページへ
  • 251.
    251 / 277  BがAの右側にある場合の条件分岐  (1) BがAと同じSplay Treeに属する場合 ◦ この場合は、AはBの子孫ということになるので、AとBのLCA はBになる。
  • 252.
    252 / 277  BがAの右側にある場合の条件分岐  (1) BがAと同じSplay Treeに属する場合 ◦ この場合は、AはBの子孫ということになるので、AとBのLCA はBになる。  (2) BがAと異なるSplay Treeに属する場合 ◦ 一番一般的な場合。 ◦ Bから上に辿り、Aと同じSplay Treeに到達したところの頂点が、 AとBのLCAになる。
  • 253.
    253 / 277  BがAの右側にある場合の条件分岐  (1) BがAと同じSplay Treeに属する場合 ◦ この場合は、AはBの子孫ということになるので、AとBのLCA はBになる。  (2) BがAと異なるSplay Treeに属する場合 ◦ 一番一般的な場合。 ◦ Bから上に辿り、Aと同じSplay Treeに到達したところの頂点が、 AとBのLCAになる。  これでLCAは求められた。
  • 254.
    254 / 277  クエリ1,2番に対応する「接続」「切断」は、 Link/Cut Treeの”link”, “cut” に対応する。
  • 255.
    255 / 277  クエリ1,2番に対応する「接続」「切断」は、 Link/Cut Treeの”link”, “cut” に対応する。  (1) Link操作 – AをBの子にする ◦ AとBをsplayLC()しておいてから、Aの親として(dashedで)Bを 設定するだけ。 ◦ 計算量: AとBがLink/Cut Treeにおける根にあるので、Bのサ イズが高々𝑁増える程度。これによってポテンシャルは 𝑂(log 𝑁) しか増えない。
  • 256.
    256 / 277  クエリ1,2番に対応する「接続」「切断」は、 Link/Cut Treeの”link”, “cut” に対応する。  (2) Cut操作 – Aを親から切り離す ◦ AをsplayLC()してからAの右の子を切り離す。 ◦ 計算量:ポテンシャルは明らかに減っている。
  • 257.
    257 / 277  以上がLink/Cut Treeによる満点解法。
  • 258.
    258 / 277  Euler Tour Tree と Link/Cut Tree は動的木の筆頭
  • 259.
    259 / 277  Euler Tour Tree と Link/Cut Tree は動的木の筆頭  今回はどちらを選ぶべきだったか?
  • 260.
    260 / 277  Euler Tour Tree と Link/Cut Tree は動的木の筆頭  今回はどちらを選ぶべきだったか?  (他の問題は解き終わっているとして)
  • 261.
    261 / 277  Euler Tour Tree ◦ 知識: ◦ 実装:  Link/Cut Tree ◦ 知識: ◦ 実装:
  • 262.
    262 / 277  Euler Tour Tree ◦ 知識: 過去にも出題済みの知識の組合せ。 ◦ 実装:  Link/Cut Tree ◦ 知識: ◦ 実装:
  • 263.
    263 / 277  Euler Tour Tree ◦ 知識: 過去にも出題済みの知識の組合せ。 ◦ 実装: 組み合わせてはいけないものを組み合わせてしまった 感じ  Link/Cut Tree ◦ 知識: ◦ 実装:
  • 264.
    264 / 277  Euler Tour Tree ◦ 知識: 過去にも出題済みの知識の組合せ。 ◦ 実装: 組み合わせてはいけないものを組み合わせてしまった 感じ  Link/Cut Tree ◦ 知識: 必須 ◦ 実装:
  • 265.
    265 / 277  Euler Tour Tree ◦ 知識: 過去にも出題済みの知識の組合せ。 ◦ 実装: 組み合わせてはいけないものを組み合わせてしまった 感じ  Link/Cut Tree ◦ 知識: 必須 ◦ 実装: 頂点にデータを持たせなくてよいなど、この問題におい ては極めて有利
  • 266.
    266 / 277  Euler Tour Tree ◦ 知識: 過去にも出題済みの知識の組合せ。 ◦ 実装: 組み合わせてはいけないものを組み合わせてしまった 感じ  Link/Cut Tree ◦ 知識: 必須 ◦ 実装: 頂点にデータを持たせなくてよいなど、この問題におい ては極めて有利  知っているならLink/Cut を書くべきだったかもしれな い
  • 267.
    267 / 277  Link/Cutを学ぶべきか?
  • 268.
    268 / 277  Link/Cutを学ぶべきか? ◦ Link/Cutでなければ出来ない、という問題は、恐らくない
  • 269.
    269 / 277  Link/Cutを学ぶべきか? ◦ Link/Cutでなければ出来ない、という問題は、恐らくない ◦ しかし、Link/Cutを使うと有利な問題は実際に存在している
  • 270.
    270 / 277  qnighyからの提案
  • 271.
    271 / 277  qnighyからの提案 ◦ 合宿参加者の大半にとっては、Link/Cut Treeを習得するコ ストが高くつく上に、他の学習をしたほうがずっと為になると 思う。
  • 272.
    272 / 277  qnighyからの提案 ◦ 合宿参加者の大半にとっては、Link/Cut Treeを習得するコ ストが高くつく上に、他の学習をしたほうがずっと為になると 思う。 ◦ より上位の人や、単純に興味があるという人に関しては、こ の限りではない。
  • 273.
    273 / 277  qnighyからの提案 ◦ 合宿参加者の大半にとっては、Link/Cut Treeを習得するコ ストが高くつく上に、他の学習をしたほうがずっと為になると 思う。 ◦ より上位の人や、単純に興味があるという人に関しては、こ の限りではない。 ◦ いずれにせよ、学習するつもりなら、身に付けるために問題 を解くべきだろう。
  • 274.
    274 / 277  JOI2010春合宿 Day4 “Highway”  JOI2012本選 問題5 “Festivals in JOI Kingdom”  IOI2011 Day2 “Elephants”  IJPC2012 Day3 “Animals2”
  • 275.
    275 / 277  完全制覇・ツリー上でのクエリ処理技法 [iwiwi] http://topcoder.g.hatena.ne.jp/iwiwi/20111205/13 23099376  プログラミングコンテストでのデータ構造 2 ~動的木 編~ [iwiwi] http://www.slideshare.net/iwiwi/2-12188845  蟻本 [iwiwi]
  • 276.
    276 / 277  Daniel D. Sleator and Robert E. Tarjan, A Data Structure for Dynamic Trees, Journal of Computer and System Sciences, Volume 26 Issue 3, June 1983, pp. 362 – 391  Daniel D. Sleator and Robert E. Tarjan, Self-adjusting binary search trees, Journal of the ACM, Volume 32 Issue 3, July 1985, pp. 652 – 686
  • 277.
    277 / 277 12 10 8 6 4 2 0 0 10 20 30 40 50 60 70 80 90 100