整理一些树的,基本的,简单的一些知识。
先写一下关于树的许多定义。
树,父节点、子节点、子树、祖先、兄弟、根节点、叶节点、直径、路径、重心、直径、最近公共祖先、生成树、dfs序,树形dp等
1、最近公共祖先
一般用倍增求LCA(Least Common Ancestors)。
按照朴素的做法,就是深的点跳到同一高度,然后两个点一齐往上跳。跳到同一位置。
这样其实不慢,一般的树,深度为logn,所以这个复杂度可以是logn。但如果树成了一条链,那么复杂度就是O(n)。
而倍增求LCA,和这个思想是一样的,深的点跳到同一高度,然后两个点一齐往上跳。不过它不是一个点一个点的跳,看下面。
对于任何的数字都可以分解成2的次幂相加的形式(7 = 22+21+20,10 = 23+21...),证明也很简单,任何一个十进制都可以分解成二进制表示。
那么对于跳的任何高度都可以用二进制表示(跳7个,7 = 22+21+20,跳10个10 = 23+21),那么我们预处理出每个点往上跳2的次幂的点,所到达的点是谁就好了。
然后和上面一样跳。
这样明显比上面的快。
贴一下代码 luogu3379
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 5 using namespace std; 6 const int N = 1000100; 7 8 struct Edge { 9 int v,nxt; 10 } e[N]; 11 int f[N][25]; 12 int deth[N]; 13 int head[N]; 14 int n,m,s,tot,a,b,d; 15 16 void add(int u,int v) { 17 tot++; 18 e[tot].v = v; 19 e[tot].nxt = head[u]; 20 head[u] = tot; 21 } 22 23 void dfs(int x) { 24 for (int i=head[x]; i; i=e[i].nxt) { 25 int v = e[i].v; 26 if(!deth[v]) 27 { 28 deth[v] = deth[x]+1; 29 f[v][0] = x; 30 dfs(v); 31 } 32 } 33 } 34 35 void init() { 36 for (int j=1; j<=20; ++j) 37 for (int i=1; i<=n; ++i) 38 f[i][j] = f[f[i][j-1]][j-1]; 39 } 40 41 int lca(int u,int v) { 42 if (deth[u] < deth[v]) swap(u,v); 43 /*for (int i=20; i>=0; --i) 44 { 45 if (deth[f[u][i]] >= deth[v]) 46 u = f[u][i]; 47 }*/ 48 d = deth[u]-deth[v]; 49 for (int i=0; i<=20; ++i) { 50 if ((1<<i) & d) 51 u = f[u][i]; 52 } 53 if (u==v) return u; 54 for (int i=20; i>=0; --i) { 55 if(f[u][i] != f[v][i]) { 56 u = f[u][i]; 57 v = f[v][i]; 58 } 59 } 60 return f[u][0]; 61 } 62 63 int main() { 64 scanf("%d%d%d",&n,&m,&s); 65 for (int i=1; i<n; ++i) { 66 scanf("%d%d",&a,&b); 67 add(a,b); 68 add(b,a); 69 } 70 deth[s] = 1; 71 f[s][0] = 0; 72 dfs(s); 73 init(); 74 for (int i=1; i<=m; ++i) { 75 scanf("%d%d",&a,&b); 76 printf("%d\n",lca(a,b)); 77 } 78 return 0; 79 }