樹鏈剖分求LCA

2022-08-21 13:45:13 字數 3080 閱讀 1465

這裡先推薦兩道練習的裸題

首先是求點

【codevs4605】 lca

就是求兩個點的公共祖先,每次詢問xor上上乙個詢問的答案。

先是兩遍dfs:

dfs1:把dep、siz、son求出來

dfs2:求出top和w

siz[v]表示以v為根的子樹的節點數

dep[v]表示v的深度(根深度為1)

top[v]表示v所在的鏈的頂端節點

fa[v]表示v的父親

son[v]表示與v在同一重鏈上的v的兒子節點

w[v]結點編號

1

int lca(int x,inty)2

11return dep[x]x : y;

12 }

下面來分析這個演算法。

1:如果top[a]==top[b],說明a和b在同一條重鏈上,顯然它們中深度較小的點即為它們的最近公共祖先。

2:如果top[a]!=top[b],(說明a,b在不同的重鏈上)且a的深度較大,則此時a,b的lca不可能在a所在的重鏈上。

因為如果a,b的lca在a所在重鏈上,那麼top[a]顯然也為a,b的公共祖先,則若dep[up[a]]]>dep[b],則顯然不可能,若dep[up[a]]]<=dep[b],則設dep[up[a]]]為d,因為d>=dep[b],所以我沿著b向上搜尋,在深度d處也可以找到乙個點c為b的祖先,又因為a,b不在同一條重鏈上,所以top[a]!=c,這就意味著在同一深度,b有兩個不同的祖先,這是不可能的(因為是一棵樹),所以,lca不可能在a所在的重鏈上。所以我們可以將a上公升到up[a]的父節點,這時a,b的lca沒有變化。

3:若果top[a]!=top[b],且b的深度較大,同理我們可將b上公升到up[b]的父節點。

4: a,b不停地上公升,所以一定可以找到a,b的lca。

因為我們知道,在樹中任意一點到根的路徑上,重鏈數不會超過(logn),所以我們可以在o(logn)的時間複雜度內,將a,b的lca求出來。

(證明來自:

1 #include2 #include3 #include4 #include5 #include6 #include7

using

namespace

std;89

#define n 100010

1011

struct

edge

12e[n<<1

];15

int head[n<<1

];16

intcnt;

1718

intfa[n],dep[n],son[n],top[n],siz[n],pos[n];

1920

intn,m;

2122

intx,y;

23int

ans;

2425

introot;

2627

void link(int x,int

y)28

;30 head[y]=cnt;31}

3233

void dfs(int x,int

d)3444}

4546

void dfs2(int x,int

d)47

5657

int lca(int x,int

y)58

67return dep[x]x : y;68}

6970

intmain()

7181

link(i,fa[i]);82}

83 dfs(root,1

);84 cnt=0

;85 top[root]=root;

86dfs2(root,root);

87 scanf("

%d",&m);

88while (m--)

8996

return0;

97 }

codevs4605

然後是求值

【codevs2370】 小機房的樹

大意:求樹上兩點距離

我寫了樹鏈剖分

1 #include2 #include3 #include4 #include5 #include6 #include7

using

namespace

std;89

#define n 100010

1011

struct

edge

12e[n<<1

];15

int head[n<<1

];16

intcnt;

1718

intfa[n],dep[n],son[n],top[n],siz[n],pos[n];

1920

intn,m;

2122

intx,y;

23int

ans;

2425

introot;

2627

void link(int x,int

y)28

;30   head[y]=cnt;31}

3233

void dfs(int x,int

d)3444}

4546

void dfs2(int x,int

d)47

5657

int lca(int x,int

y)58

67return dep[x]x : y;68}

6970

intmain()

7181

link(i,fa[i]);82}

83   dfs(root,1

);84   cnt=0

;85   top[root]=root;

86dfs2(root,root);

87   scanf("

%d",&m);

88while (m--)

8996

return0;

97 }

codevs2370

複雜度o(qlogn),還是比較快的。

樹鏈剖分求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 ...

樹鏈剖分 入門(求LCA)

顧名思義,樹鏈剖分就是將樹剖分成一條一條的鏈,之後快速的操作鏈。樹鏈剖分的複雜度為,預處理兩遍dfs,o n 複雜度 之後查詢或者修改操作每一條鏈為o log n 因為剖分出來的鏈一共有log n 條,當然如果在鏈上再套個線段樹,那每次操作就是o log2 n 之後,下文只利用樹鏈剖分求樹上兩點的最...