首页 > 代码库 > [算法整理]树上求LCA算法合集

[算法整理]树上求LCA算法合集

1#树上倍增

  以前写的博客:http://www.cnblogs.com/yyf0309/p/5972701.html

  预处理时间复杂度O(nlog2n),查询O(log2n),也不算难写。

2#st表(RMQ)

  首先对一棵树进行dfs,得到欧拉序列,记录下每个节点的第一次出现位置。

技术分享

  (先序遍历这棵树,访问到的节点(无论是从深的一层返回还是父节点访问)就加入到序列中,序列长度为2 * n - 1)

  根据欧拉序列神奇的特性,两个点第一次出现的位置之间,深度最小的一个点,是这两个点LCA(反正我是不会证明)。于是可以干什么呢?就建st表就行了。

  预处理时间复杂度O(2nlog2(2n) + n),查询时间复杂度O(log2n)。

codevs 商务旅行:

技术分享
  1 /**  2  * codevs  3  * Problem#1036  4  * Accepted  5  * Time:52ms  6  * Memory:2736k  7  */  8 #include<iostream>  9 #include<sstream> 10 #include<cstdio> 11 #include<cmath> 12 #include<cstdlib> 13 #include<cstring> 14 #include<cctype> 15 #include<queue> 16 #include<set> 17 #include<map> 18 #include<stack> 19 #include<vector> 20 #include<algorithm> 21 #ifndef    WIN32 22 #define    AUTO "%I64d" 23 #else 24 #define AUTO "%lld" 25 #endif 26 using namespace std; 27 typedef bool boolean; 28 #define smin(a, b) (a) = min((a), (b)) 29 #define smax(a, b) (a) = max((a), (b)) 30 template<typename T> 31 inline void readInteger(T& u){ 32     char x; 33     int aFlag = 1; 34     while(!isdigit((x = getchar())) && x != -); 35     if(x == -){ 36         aFlag = -1; 37         x = getchar(); 38     } 39     for(u = x - 0; isdigit((x = getchar())); u = u * 10 + x - 0); 40     ungetc(x, stdin); 41     u *= aFlag; 42 } 43  44 template<typename T>class Matrix{ 45     public: 46         T *p; 47         int lines; 48         int rows; 49         Matrix():p(NULL){    } 50         Matrix(int rows, int lines):lines(lines), rows(rows){ 51             p = new T[(lines * rows)]; 52         } 53         T* operator [](int pos){ 54             return (p + pos * lines); 55         } 56 }; 57 #define matset(m, i, s) memset((m).p, (i), (s) * (m).lines * (m).rows) 58  59 ///map template starts 60 typedef class Edge{ 61     public: 62         int end; 63         int next; 64         Edge(const int end = 0, const int next = 0):end(end), next(next){} 65 }Edge; 66 typedef class MapManager{ 67     public: 68         int ce; 69         int *h; 70         Edge *edge; 71         MapManager(){} 72         MapManager(int points, int limit):ce(0){ 73             h = new int[(const int)(points + 1)]; 74             edge = new Edge[(const int)(limit + 1)]; 75             memset(h, 0, sizeof(int) * (points + 1)); 76         } 77         inline void addEdge(int from, int end){ 78             edge[++ce] = Edge(end, h[from]); 79             h[from] = ce; 80         } 81         inline void addDoubleEdge(int from, int end){ 82             addEdge(from, end); 83             addEdge(end, from); 84         } 85         Edge& operator[] (int pos) { 86             return edge[pos]; 87         } 88 }MapManager; 89 #define m_begin(g, i) (g).h[(i)] 90 ///map template ends 91  92 int n, m; 93 int cnt = 0; 94 Matrix<int> st; 95 int* seq; 96 int *dep, *app; 97 MapManager g; 98 const int P = 15; 99 int *mlog2;100 101 inline void init() {102     readInteger(n);103     g = MapManager(n, 2  * n);104     seq = new int[(const int)(2 * n + 1)];105     dep = new int[(const int)(n + 1)];106     app = new int[(const int)(n + 1)];107     for(int i = 1, a, b; i < n; i++){108         readInteger(a);109         readInteger(b);110         g.addDoubleEdge(a, b);111     }112     dep[0] = 0;113 }114 115 void dfs(int node, int f) {116     seq[++cnt] = node;117     dep[node] = dep[f] + 1;118     app[node] = cnt;119     for(int i = m_begin(g, node); i != 0; i = g[i].next) {120         int& e = g[i].end;121         if(e == f)    continue;122         dfs(e, node);123         seq[++cnt] = node;124     }125 }126 127 inline void init_log() {128     mlog2 = new int[(const int)(2 * n + 1)];129     mlog2[1] = 0;130     for(int i = 2; i <= 2 * n; i++)131         mlog2[i] = mlog2[i / 2] + 1; 132 }133 134 inline void init_st() {135     init_log();136     st = Matrix<int>(cnt, mlog2[cnt] + 1);137     for(int i = 1; i <= cnt; i++)138         st[i][0] = seq[i];//,cout << i << " " << 0 << ":" << st[i][0] << endl;139     for(int j = 1; j <= P; j++)140         for(int i = 1; i + (1 << j) - 1 <= cnt; i++)141             st[i][j] = (dep[st[i][j - 1]] < dep[st[i + (1 << (j - 1))][j - 1]]) ? (st[i][j - 1]) : (st[i + (1 << (j - 1))][j - 1]);142 //            cout << i << " " << j << ":" << st[i][j] << endl;143 }144 145 inline int lca(int a, int b) {146     if(app[a] > app[b])    swap(a, b);147     int pos = mlog2[app[b] - app[a] + 1];148     int u = st[app[a]][pos];149     int v = st[app[b] - (1 << pos) + 1][pos];150     return (dep[u] > dep[v]) ? (v) : (u);151 }152 153 int last;154 int dist;155 inline void solve() {156     readInteger(m);157     readInteger(last);158     for(int i = 1, a; i < m; i++){159         readInteger(a);160         int l = lca(a, last);161         dist += dep[a] + dep[last] - 2 * dep[l];162         last = a;163     }164     printf("%d", dist);165 }166 167 int main() {168     init();169     dfs(1, 0);170     init_st();171     solve();172     return 0;173 }
商务旅行(st表)

3#Tarjan算法(离线算法)

  Tarjan需要一个并查集来辅助。

  算法思路大概是这样:

  init:初始化并查集,对查询建一个邻接链表,还是按照存边的方式,要双向的,再用一个数组记录第i个询问是否解决了。

  1.访问子树

  2.每次访问结束后将子树的并查集中的父节点指向当前节点。

  3.子树都访问完了后,开始处理以该节点为起点的询问,如果终点已经被访问过了并且没有被解决,那么这个点和终点的LCA是终点在并查集中的父节点,然后再记录是否解决的那个数组中把对应位置设上true。

  (画画图,试着举点反例(当然不存在)还是很容易理解的)

技术分享
  1 #include<iostream>  2 #include<sstream>  3 #include<cstdio>  4 #include<cmath>  5 #include<cstdlib>  6 #include<cstring>  7 #include<cctype>  8 #include<queue>  9 #include<set> 10 #include<map> 11 #include<stack> 12 #include<vector> 13 #include<algorithm> 14 #ifndef    WIN32 15 #define    AUTO "%I64d" 16 #else 17 #define AUTO "%lld" 18 #endif 19 using namespace std; 20 typedef bool boolean; 21 #define smin(a, b) (a) = min((a), (b)) 22 #define smax(a, b) (a) = max((a), (b)) 23 template<typename T> 24 inline void readInteger(T& u){ 25     char x; 26     int aFlag = 1; 27     while(!isdigit((x = getchar())) && x != -); 28     if(x == -){ 29         aFlag = -1; 30         x = getchar(); 31     } 32     for(u = x - 0; isdigit((x = getchar())); u = u * 10 + x - 0); 33     ungetc(x, stdin); 34     u *= aFlag; 35 } 36  37 typedef class Edge { 38     public: 39         int end; 40         int next; 41         int w; 42         Edge(const int end = 0, const int next = 0, const int w = 0):end(end), next(next), w(w){        } 43 }Edge; 44  45 typedef class MapManager{ 46     public: 47         int ce; 48         Edge* edges; 49         int* h; 50         MapManager():ce(0), edges(NULL), h(NULL){        } 51         MapManager(int points, int limit):ce(0){ 52             edges = new Edge[(const int)(limit + 1)]; 53             h = new int[(const int)(points + 1)]; 54             memset(h, 0, sizeof(int) * (points + 1)); 55         } 56         inline void addEdge(int from, int end, int w){ 57             edges[++ce] = Edge(end, h[from], w); 58             h[from] = ce; 59         } 60         inline void addDoubleEdge(int from, int end, int w){ 61             addEdge(from, end, w); 62             addEdge(end, from, w); 63         } 64         Edge& operator [](int pos){ 65             return edges[pos]; 66         } 67 }MapManager; 68 #define m_begin(g, i) (g).h[(i)] 69  70 typedef class union_found{ 71     public: 72         int *f; 73         union_found():f(NULL) {} 74         union_found(int points) { 75             f = new int[(const int)(points + 1)]; 76         } 77         int find(int x) { 78             if(f[x] != x)    return f[x] = find(f[x]); 79             return f[x]; 80         } 81         void unit(int fa, int so) { 82             int ffa = find(fa); 83             int fso = find(so); 84             f[fso] = ffa; 85         } 86         int& operator [](int pos){ 87             return f[pos]; 88         } 89 }union_found; 90  91 int n, m; 92 MapManager g; 93 MapManager q; 94 int *results; 95 boolean* enable; 96 int *querya, *queryb; 97 union_found uf; 98 boolean* visited; 99 int* dist;100 101 inline void init(){102     readInteger(n);103     g = MapManager(n, 2 * n);104     for(int i = 1, a, b, c; i < n; i++){105         readInteger(a);106         readInteger(b);107         readInteger(c);108         g.addDoubleEdge(a, b, c);109     }110     readInteger(m);111     q = MapManager(n, 2 * m);112     querya = new int[(const int)(m + 1)];113     queryb = new int[(const int)(m + 1)];114     results = new int[(const int)(m + 1)];115     enable = new boolean[(const int)(m + 1)];116     dist = new int[(const int)(n + 1)];117     uf = union_found(n);118     visited = new boolean[(const int)(n + 1)];119     memset(visited, false, sizeof(boolean) * (n + 1));120     memset(enable, true, sizeof(boolean) * (m + 1));121     for(int i = 1; i <= m; i++){122         readInteger(querya[i]);123         readInteger(queryb[i]);124         q.addDoubleEdge(querya[i], queryb[i], i);125     }126     dist[1] = 0;127 }128 129 void tarjan(int node, int f){130     uf[node] = node;131     visited[node] = true;132     for(int i = m_begin(g, node); i != 0; i = g[i].next){133         int& e = g[i].end;134         if(e == f)    continue;135         dist[e] = dist[node] + g[i].w;136         tarjan(e, node);137         uf[e] = node;138     }139     for(int i = m_begin(q, node); i != 0; i = q[i].next) {140         int& e = q[i].end;141         if(visited[e] && enable[q[i].w]){142             int lca = uf.find(e);143             results[q[i].w] = lca;144             enable[q[i].w] = false;145         }146     }147 }148 149 inline void solve(){150     tarjan(1, 0);151     for(int i = 1; i <= m; i++){152         int dis = dist[querya[i]] + dist[queryb[i]] - 2 * dist[results[i]];153         printf("%d\n", dis);154     }155 }156 157 int main(){158     init();159     solve();160     return 0;161 }
Tajan

 

[算法整理]树上求LCA算法合集