首先我要吐槽機房的辣基供電情況,我之前寫了一上午,馬上就要完成的時候突然停電,然後\(gg\)成了送鏈剖分
其次,我沒歧視\(tarjan lca\)
理解較為簡單的一種方法,但速度略慢
每個數字都可以拆成幾個二的整數次的和,我們可以找出每個數字是由哪幾個二的整數次的數合成的
比如說\(14 _ = 1110_2 = 1000 _2 + 100 _2 + 10 _2 = 8 _ + 4 _ + 2 _\)
那麼我們如果要統計一段長度為十四的區間的最小值,我們就可以先統計前八個數的最小值,再統計之後的四個,再統計之後的兩個。
我們可以用\(f[i][j]\)表示從\(i\)開始往後\(2^j\)長度的最小值
給寧康康**
for( int i = 1; i <= 23; i++ )
}
下面這個東西是啥意思呢
f[i][j - 1], f[a[i][j - 1]][j - 1]
\(2^j = 2^ +2^\) 比如說 \(2^4 = 2 ^ 3 +2 ^ 3\)
在每個葉節點到根節點的鏈上做倍增
演示一哈(如果開啟我的部落格就會發現我這種蒟蒻說不清話只會畫圖
應該比較顯然吧???
我們在求\(lca\)前\(dfs\)一遍,統計出每個葉節點的\(f[i][1]\)(也就是父節點)和\(dep[i]\)(就是該節點所處深度,規定根節點深度為1)。然後跑一遍倍增,預處理每個葉節點的向上\(2^i\)個祖宗是誰。
然後倍增求\(lca\),我們可以先看兩個點是否在同一深度,不在的話就把比較低的那個點往上走一走,直到走到同一深度。注意在跳的時候要從大到小列舉,給寧康康**,寧再把上面的\(14\)那個例子帶進去從\(1\)到\(20\)列舉一下子就懂了
inline int lca( int x, int y )
if( x == y ) return x;
for( rint i = 20; i >= 0; i-- )
return f[x][0];
}
#includeusing namespace std;
#define rint register int
int n, m, s, cnt, dep[500005], f[500005][23], head[500005];
struct edgea[500005<<1];
inline int read( void )
while( ch >= '0' && ch <= '9' )
return re * f;
}inline void addedge( int x, int y )
inline void dfs( int x, int fa )
for( rint i = head[x]; i; i = a[i].nxt )
return ;
}inline int lca( int x, int y )
if( x == y ) return x;
for( rint i = 20; i >= 0; i-- )
return f[x][0];
}int main( void )
dfs( s, 0 );
int u, v;
for( rint i = 1; i <= m; i++ )
return 0;
}
樹鏈剖分其實有好多種剖分方法,但這裡只介紹輕重邊剖分
乙個節點只能有乙個重兒子。
鏈 : 連續的重/輕邊構成一條鏈。(圖中從\(1\)到\(14\)即一條重鏈)
\(dep[i]\) : \(i\)節點的深度,規定根節點深度為\(1\)。
\(fa[i]\) : \(i\)節點的父親。
\(son[i]\) : \(i\)節點的重兒子。
\(siz[i]\) : 以\(i\)節點為根的子樹的大小。
\(top[i]\) : \(i\)所在鏈的根。(圖中從\(1\)到\(14\)的鏈的根為\(1\))
重邊 : 以\(i\)節點的兒子中\(siz\)最大的兒子到\(i\)的連邊,即圖中的粗邊(該兒子也叫重兒子)。
輕邊 : 處重邊外的其他邊。
首先\(dfs\)一遍,求出\(dep\),\(fa\),\(son\),\(siz\),容易實現,給寧康**
inline void dfs1( int now, int father, int de )
}}
然後再\(dfs\)一遍,處理出每個點的\(top\),也就是所在鏈的頂點,其中輕鏈的頂點是它自己。
給寧康康**
inline void dfs2( int now, int topf )
}
如果乙個點要跳到它的\(lca\),就一定會跳到它的\(lca\)所在鏈(廢話……
那麼我們要判定是否已經找到\(lca\),就只需要看當前兩點\(xy\)是否在同一條鏈上,其實就是看兩個點的\(top\)是否相等,如果不相等的話,我們就讓深度大的點一次性跳完一整條重鏈,然後再跳一步,走上另一條重鏈,再重複以上比較\(top\),跳重鏈的過程
我們可以發現非葉節點一定在某條重鏈上,所以我們一次性跳完一條重鏈,再跳一步,就會跳上另一條重鏈,所以查詢\(lca\)的複雜度是小於\(logn\)的
給寧康康**
inline int lca( int x, int y )
if( dep[x] > dep[y] ) return y;
return x;
}
全部**:
#includeusing namespace std;
#define rint register int
int t, n, m;
int son[1000010], fa[1000010], siz[1000010], dep[1000010], top[1000010];
vector< int > vec[1000010];
inline int read( void )
while( ch >= '0' && ch <= '9' )
return re * f;
}inline void dfs1( int now, int father, int de ) }}
inline void dfs2( int now, int topf )
}inline int lca( int x, int y )
if( dep[x] > dep[y] ) return y;
return x;
}int main( void )
dfs1( 1, 1, 1 );
dfs2( 1, 1 );
for( rint i = 1; i <= m; i++ )
return 0;
}
如果要求樹上兩點最短距離,可以求\(lca\)
\(dis = dep[x] + dep[y] - 2 * lca\)
樹鏈剖分求LCA
這裡先推薦兩道練習的裸題 首先是求點 codevs4605 lca 就是求兩個點的公共祖先,每次詢問xor上上乙個詢問的答案。先是兩遍dfs dfs1 把dep siz son求出來 dfs2 求出top和w siz v 表示以v為根的子樹的節點數 dep v 表示v的深度 根深度為1 top v ...
樹鏈剖分求lca
題目描述 給一棵有根樹,以及一些詢問,每次詢問樹上的2 個節點a b,求它們的最近公共祖先.輸入第一行乙個整數n.接下來n 個數,第i 個數fi 表示i 的父親是fi.若fi 0,則i 為樹根.接下來乙個整數m.接下來m 行,每行2 個整數a b,詢問節點 a xor lastans bxor la...
樹鏈剖分(1) 樹剖求LCA
先看乙個樹剖的經典應用 初始化 先dfs一遍子樹,統計出每乙個點x的重兒子son x 和以x為根節點的子樹的大小siz x 這裡選擇x中子樹大小最大的兒子作為它的重兒子,第二遍dfs劃分樹鏈 重兒子與其父親節點劃分到一條鏈。其他的兒子為x的輕兒子,但屬於新的鏈的頂端元素。void dfs1 int ...