這裡先推薦兩道練習的裸題
首先是求點
【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 #include7codevs4605using
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】 小機房的樹
大意:求樹上兩點距離
我寫了樹鏈剖分
1 #include2 #include3 #include4 #include5 #include6 #include7codevs2370using
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 }
複雜度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 之後,下文只利用樹鏈剖分求樹上兩點的最...