LCA倍増方法
テンプレート:ルオゴ P3379 [テンプレート]最近の共通の祖先(LCA)
今日は神様の話を少し時間を走った、そして今謙虚に詰め込む(シャーレ)
LCAは、最新の共通祖先(最小共通祖先)を指します。
最も単純なアルゴリズムは間違いなく現れ、一つ一つ二つの点から上がっている最初の2点はポイントが横断している 2点LCAです。
しかし、時間が非常に長いです。
だから、倍増のみ使用することは、役割を倍にすることで必要な複雑さを軽減するために2つの点を上昇します。
その層は、その後、深い[LCA-1]をジャンプ、同じレイヤージャンプの深い二つの異なる点、及び次いで層はLCAである最大移動:一般的なプロセスです。
この方法は、層2の各i番目の層までのジャンプのジャンプを加速することで、層の数は、時間複雑度は、(N)LOG2となるように、二進数に変換されます。
F [I] [J](への2つの配列の後に2 ^ jは、層Iから到着アップ後)ディープ[I](点Iのツリーの深さ)。
深い[i]は、DFSで得られました。
再帰によってF [I] [J]、F [i]は[J] = F [F [I] [J-1] [J-1]。初期化F [i]が[0]ツリー全体をトラバースする時に決定されてもよいです。
その後、同時に2つが再び近づいて。私は2つの点x、yが、それは式のように決定された最大ジャンプすることができると仮定すると、それぞれの最上位ビットの列挙を開始します。
もし([X] [I] = F!F [Y] [I]) { X = F [X] [I]。 Y = F [Y] [I]。 }
2は2 ^である場合、私はそれからジャンプアップする時点の後に同じ層未満のジャンプアップ。これはなぜでしょうか?2 ^ i層までのジャンプが、でも同じポイントに、場合ので、それは必ずしも2点LCAではありません。
そうすることで、最終的にLCAの下の層に到達します。私たちは、再び2層にジャンプアップ。LCAが決定しました。
そして、これは私は非常に困惑疑問が生じます。
なぜ最終的にLCAの下の層に届くのだろうか?
私たちは、同じポイントにジャンプし、2 ^ jの層をジャンプアップ、B点という前提からスタート。ジャンプしないでください。最大2(J-1)^層ジャンプ、A 'B'に、それぞれ、アップジャンプ、同じ点ジャンプではありません。明らかに、このような状況は確かに存在します。その後、ジャンプ元のポイントにジャンプしないという決定にさらにその後、A「B」からとは、明らかに2 ^(J-1)層をジャンプ。まあ、その点は、LCAであってもよいし、右、そこではないかもしれませんか?したがって、Aから'B'層のLCA所望の数の最大ジャンプ≤2^(J-1)Aです。2進数に層のXの数に換言すれば、A「B」、J-2ビット(先頭のゼロ点を置くことも同じジャンプである、とすることができます)。この時点で、ちょうどJ-2ビットを列挙する。だから、先行ゼロが低減されていない、そのように、あなたは、層の数との差が、最終的にゼロになることを見つけ切り倒して、あなたは最終的に層Xを到達します
おそらくあなたの叔父(父の弟)が祖先は直系である必要があります見つけるためにここにあなたの先祖ではありません。
なぜ、層数の違いは、最終的にはX 0になるのだろうか?
まず、先行ゼロが減算されていない、ことを証明します。層の数との差はX層がX」であり、あなたがy層をジャンプアップする準備ができていることが想定されます。層数に右、それはジャンプしませんLCA X + 1、及びLCAアップのポイントとは?1 +(代わりに、LCAダウンポイント場合、つまり、層の数<=のX、即ち、Y <X「+ 1、それは最大ジャンプする)、そうであれば、Y> = x」は、それが起動していないであろうジャンプ。
明らかに、場合X「ビット0の、及び先行ゼロ、次いでのみ証明Xに属する」+ 1 <= Y。これがあることを証明することは非常に容易である(10000 Yを仮定し、そしてX「を満たす最大01111の条件)。だから私は、先行ゼロが減算されていない、保証します。
その後、我々はかつてこの1つは間違いなく差し引かれます、x「の最初の桁が1で列挙し、証明します。同様に、そのYを仮定すると、+ 1「10,000 Y <Xの最小値の条件を満たし」10000であり、そしてx。
二つ一緒に、先行ゼロは、減算ない減算ものを列挙するために、この差の最終層は、0証拠となります。
コード:
書式#include <cstdioを> する#include <CStringの> の#include <スタック> の#include <アルゴリズム> 書式#include <cmath> に#define MAXN 500010 の#include <キュー> 使用して 名前空間はstdを、 INT [MAXN] [F 21 ]、ヘッド[MAXN]、深い[MAXN]、CNT、N、M、RT。 構造体のエッジ { int型V、次。 } E [MAXN << 1 ]。 ボイド追加(INT U、INT V) { E [ ++ CNT] .V = V。 E [CNT] .next = 頭部[U]。 頭部[U] = CNT。 } ボイド DFS(int型のx、int型FA) { F [X] [ 0 ] = FA。 深い[X] =深い[FA] + 1 。 以下のために(int型 ; I I = I =ヘッド[X] E [I] .next) { int型、V = E [I] .V。 もし(== FA V)続けます。 DFS(V、X) } } ボイドは、init() { 用(INT J = 1 ;(1 << J)<= N; ++ j)は { 用(のint i = 1 ; iが<= N; ++ I) { 場合(深い[I]> =(1 << J)) { F [I] [J] = F [F [I] [J- 1 ] [J- 1 ]。 } } } } int型の照会(int型のx、int型のY) { 場合(深い[X] < 深い[Y])スワップ(X、Y) INT D =深い[X] - ディープ[Y]。 用(INT J = 20、jは> = 0 - J。)であれば(D&(1<< J))X = F [X] [J]。 もし(x == y)は戻りX。 用(INT J = 20、jは> = 0 - ; J) { 場合(![X] [J] = F F [Y] [J]) { X = F [X] [J]。 Y = F [Y] [J]。 } } リターン F [X] [ 0 ]。 } int型のmain() { int型Uを、V。 scanf関数(" %D%D%D "、&N、&M、&RT)。 以下のための(int型i = 1 ; iがN <; ++ i)が { scanf関数(" %D%dの"、&U、およびV)。 (V、U)を追加します。 (V、U)を追加。 } DFS(RT、0 ); その中に(); 一方、(M-- ) { int型X、Y。 scanf関数(" %dの%のD "、およびX&Y)。 printf(" %d個の\ n " 、クエリ(X、Y))。 } 戻り 0 。 }
2019-07-3122:54:32