求樹上k級祖先

2022-06-30 09:09:09 字數 2301 閱讀 4879

在學習澱粉質的時候,看到了p4292 [wc2010]重建計畫這道題,發現可以用長鏈剖分來做。就學一下。

長鏈的概念和重鏈相似

點\(x\)的長鏈指的是從\(x\)出發,到達的子樹中深度最深的兒子所經過的路徑

\(x\)所在的長鏈上的兒子稱之為長兒子♂

和輕重鏈剖分類似,通過樹上存在的長鏈,把一棵樹分割成幾條不相交的路徑

就變成了這樣

意思是一條長鏈上深度最小的節點,也就是最頂上的那個點。

對樹進行長鏈剖分之後所有的長鏈的長度的和為\(n\)

顯然任意乙個點\(x\)的\(k\)級祖先\(y\)所在長鏈長度一定\(\geq k\)。

證明:若他們兩個在同一條長鏈上

上面可能有一些,下面還可能有一點

顯然成立

若他們兩個不在同一條長鏈上

考慮最接小的的情況

因為不在同乙個長鏈上,所以\(y\)在的那一條長鏈的深度一定比\(x\)在的那條長鏈的深度\(+k\)要長

所以\(y\)在的那一條長鏈的長度\(\geq k\)。

由於長鏈剖分根據深度大小進行剖分,因此可以解決一些與深度有關的問題。

傳送門預處理

對樹進行長剖,記錄每個節點所在鏈的頂點和深度。\(o(n)\)

樹上倍增求出每個節點的\(2^n\)祖先,\(o(n\log_2 n)\)

對於每條鏈,如果其長度為\(len\),那麼在頂點處記錄頂點向上的\(len\)個祖先和向下的\(len\)個鏈上的兒子,\(o(n)\)

對於\(i\in [1,n]\)求出二進位制下的最高位\(h_i\),\(o(n)\)

對於每一次詢問\(x\)的\(k\)級祖先

利用倍增跳到\(2^\)級祖先,設剩下還有\(k'\)級,顯然\(k' < 2^(h_k)\),因此此時\(x\)所在的長鏈的長度\(len\geq 2^>k'\)

由於長鏈長度\(>k'\),因此可以將\(x=top_x\),若之後剩下的級數為正,就利用向上的陣列來求答案,反之毅然

這樣時間複雜度為\(o([1, n\log_2 n])\)了

code

#include #define ll long long

#define ui unsigned int

using namespace std;

const int maxn = 500010;

ui s;

inline ui get(ui x)

int n, q, fa[maxn][32], g[maxn]; //g[x] ---- log2 x

vectornbr[maxn];

int root, dep[maxn], d[maxn];

//dep[i]----max deep of node i

//d[i]---node i's deep

int top[maxn], son[maxn];

vectorup[maxn], dn[maxn];

void pre(int cur)

return ;

}void dfs(int cur, int p)

if(son[cur]) dfs(son[cur], p);

for(int i = 0; i < nbr[cur].size(); i ++)

return ;

}int ask(int x, int k) // x's k-level father

int main()

// cout << "asdaw";

root = nbr[0][0];

pre(root);

dfs(root, root);

// cout << "&*^*$*(";

dfs(root, root);

int lastans = 0, x = 0, k = 0;

ll ans;

for(int i = 1; i <= q; i ++)

cout << ans;

return 0;

}

P5903 模板 樹上 k 級祖先

給定一棵 n 個點的有根樹。有 q 次詢問,第 i 次詢問給定 xi,ki,要求點 xi 的 ki 級祖先。保證ki合法。一 樹鏈剖分 重鏈剖分 o n qlogn 7.46s include include include include include include include defin...

樹上倍增求LCA(最近公共祖先)

前幾天做faebdc學長出的模擬題,第三題最後要倍增來優化,在學長的講解下,嘗試的學習和編了一下倍增求lca 我能說我其他方法也大會嗎?倍增求lca father i j 表示節點i往上跳2 j次後的節點 可以轉移為 father i j father father i j 1 j 1 此處注意迴圈...

模板 最近公共祖先 樹上倍增

完整部分點這裡 有 n 種辦法可以解決lca問題,這裡我們只講一種,用樹上倍增的方法來實現 lca。在沒有學習倍增寫lca之前,你是怎麼樣求lca的呢?至少,我是老老實實地讓這兩個點一步一步往上移並找出它們的路徑第一次交匯的地方。這種方法固然可行 好想,但它的效率實在不高。但是,我們完全可以通過提高...