长链剖分 总结 【知识点】
附上自學博客鏈接
https://www.cnblogs.com/Khada-Jhin/p/9576403.html
概念:
長鏈剖分類似于重鏈剖分,只是選擇鏈的標準:
不是 子樹節點最多的兒子,而是 到葉節點的路徑最大的子節點
(所以說是長鏈)
頂點:一條長鏈上深度最小的點
性質:
對于一個有n個節點的樹,
每一個子節點的最長鏈長度不長于他的祖先節點 (顯然。。)
所有長鏈長度之和等于 n(總結點數)
證明:
一個點也算一條長鏈,每一個點在且只在一條長鏈上
從根節點走到任何一個節點,最多 n\sqrt{n}n? 條長鏈(這是個大約數)
證明:
把一條長鏈上的點抽象成一個點,(總貢獻為1)
所以,經過的每一個點都不在同一條長鏈上 ,那么走出的這條鏈長度為 k ,
有:
根節點一定有一個子節點,鏈長 ≥k\ge k≥k
根節點下一個點一定有一條子節點,鏈長 ≥k?1\ge k-1≥k?1
…以此類推
所以走下去后,沒有走過的點為 ∑1k\sum_1^k∑1k? 約為 k2/2k^2/2k2/2
所以k最大大約不過 n\sqrt {n}n?
應用:
例題傳送門
詢問一棵樹上任意一個點第k個祖先
O(n*logn)預處理+ O(1)在線回答
兩次dfs,兩次都類似重鏈剖分的dfs,只是轉換條件換成了鏈長
void dfs1(int u){len[u]=1;int v;for(int i=1;(1<<i)<=dep[u];++i){fa[u][i]=fa[fa[u][i-1]][i-1];}for(int i=head[u];i;i=e[i].nxt){v=e[i].v;if(v==fa[u][0])continue;fa[v][0]=u;dep[v]=dep[u]+1;dfs1(v);if(len[v]>mx[u])mx[u]=len[v],son[u]=v;}len[u]+=mx[u]; } void dfs2(int u){int v;aft[top[u]].push_back(u);for(int i=head[u];i;i=e[i].nxt){v=e[i].v;if(v==fa[u][0])continue;top[v]=son[u]==v ? top[u] : v;dfs2(v);} }重點是詢問環節,
維護:
我們對于每一條長鏈的頂端,維護兩個 vector 數組
pre[i][j]:pre[i][j]:pre[i][j]: 從i開始向上第j個祖先(沒錯,暴力維護。。)
aft[i][j]:aft[i][j]:aft[i][j]: 從i開始向下長鏈上第j個后代
注意:
j≥1j \ge1j≥1 && j≤len[i]j\le{len[i]}j≤len[i]
每一個頂點只維護所在長鏈長度個
由于長鏈和等于 n(第二條性質) 所以兩個維護復雜度都是 O(n)
再對 1-n(數字)維護一個 highbit(即二進制位上最靠左的1的位置,可以和lowbit對比理解)
(這個維護不會超過 O(n*log你) )
操作:
對于 詢問 第 x 號節點 的 第 k 個祖先 ,我們把目標祖先設為fa,
分類討論:
1.fa和x在一條長鏈上,即 dep[x]?dep[top[x]]≥k{dep[x]-dep[top[x]] } \ge kdep[x]?dep[top[x]]≥k
直接用top[x]top[x]top[x]的 aft 數組即可
2.fa 和 x不在一條長鏈上,即 dep[x]?dep[top[x]]<k{dep[x]-dep[top[x]] } \lt kdep[x]?dep[top[x]]<k
這個時候我們跳到x的 向上 2highbit(k)2^{highbit(k)}2highbit(k)個祖先 ,設為 y
注意,剩下的 k?2highbit(k)<2highbit(k)k-2^{highbit(k)} \lt2^{highbit(k)}k?2highbit(k)<2highbit(k)
對于 y ,y所在長鏈長度一定≥2highbit(k)\ge2^{highbit(k)}≥2highbit(k) (根據第一條性質)
所以pre和aft都可以覆蓋
對于y所在長鏈的頂端,即top[y]
如果在 fa上方,就和第一種情況相同
如果在 fa下方, 就用 top[y] 的 pre 數組 O(1)求即可
代碼:
總的代碼寫的很丑。。這里就不給了,其他的維護自己yy一下都很簡單。。
總結
以上是生活随笔為你收集整理的长链剖分 总结 【知识点】的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: TFRecord文件查看包含的所有Fea
- 下一篇: 财务模块 - 采购、接收、应付会计分录和