Commit 74dc24cf authored by ttzztztz's avatar ttzztztz
Browse files

Add LCA & Tarjan introduction

parent ac0c71b2
Loading
Loading
Loading
Loading
+109 −0
Original line number Diff line number Diff line
@@ -103,6 +103,115 @@ int main() {

### Tarjan 算法

`Tarjan算法` 是一种`离线算法`,需要使用`并查集`记录某个结点的祖先结点。做法如下:

1.  首先接受输入(邻接链表)、查询(存储在另一个邻接链表内)。查询边其实是虚拟加上去的边,为了方便,每次输入查询边的时候,将这个边及其反向边都加入到`queryEdge`数组里。
2.  然后对其进行一次DFS遍历,同时使用`visited`数组进行记录某个结点是否被访问过、`parent`记录当前结点的父亲结点。
3.  其中涉及到了`回溯思想`,我们每次遍历到某个结点的时候,认为这个结点的根结点就是它本身。让以这个结点为根节点的DFS全部遍历完毕了以后,再将`这个结点的根节点`设置为`这个结点的父一级结点`
4.  回溯的时候,如果以该节点为起点,`queryEdge`查询边的另一个结点也恰好访问过了,则直接更新查询边的LCA结果。
5.  最后输出结果。

```cpp
#include<iostream>
#include<algorithm>
using namespace std;

class Edge {
public:
	int toVertex, fromVertex;
	int next;
	int LCA;
	Edge() : toVertex(-1), fromVertex(-1), next(-1), LCA(-1) {};
	Edge(int u, int v, int n) : fromVertex(u), toVertex(v), next(n), LCA(-1) {};
};

const int MAX = 100;
int head[MAX], queryHead[MAX];
Edge edge[MAX], queryEdge[MAX];
int parent[MAX], visited[MAX];
int vertexCount, edgeCount, queryCount;

void init() {
	for (int i = 0; i <= vertexCount;i++) {
		parent[i] = i;
	}
}

int find(int x) {
	if (parent[x] == x) {
		return x;
	}
	else {
		return find(parent[x]);
	}
}

void tarjan(int u) {
	parent[u] = u;
	visited[u] = 1;

	for (int i = head[u]; i != -1;i=edge[i].next) {
		Edge& e = edge[i];
		if (!visited[e.toVertex]) {
			tarjan(e.toVertex);
			parent[e.toVertex] = u;
		}
	}

	for (int i = queryHead[u]; i != -1;i=queryEdge[i].next) {
		Edge& e = queryEdge[i];
		if (visited[e.toVertex]) {
			queryEdge[i ^ 1].LCA = e.LCA = find(e.toVertex);
		}
	}
}

int main() {
	memset(head, 0xff, sizeof(head));
	memset(queryHead, 0xff, sizeof(queryHead));

	cin >> vertexCount >> edgeCount >> queryCount;
	int count = 0;
	for (int i = 0; i < edgeCount;i++) {
		int start = 0, end = 0;
		cin >> start >> end;

		edge[count] = Edge(start, end, head[start]);
		head[start] = count;
		count++;

		edge[count] = Edge(end, start, head[end]);
		head[end] = count;
		count++;
	}

	count = 0;
	for (int i = 0; i < queryCount;i++) {
		int start = 0, end = 0;
		cin >> start >> end;

		queryEdge[count] = Edge(start, end, queryHead[start]);
		queryHead[start] = count;
		count++;

		queryEdge[count] = Edge(end, start, queryHead[end]);
		queryHead[end] = count;
		count++;
	}


	init();
	tarjan(1);

	for (int i = 0; i < queryCount;i++) {
		Edge& e = queryEdge[i * 2];
		cout << "(" << e.fromVertex << "," << e.toVertex << ") " << e.LCA << endl;
	}

	return 0;
}
```

### 转化为 RMQ 问题

首先对树进行 dfs, `dfs(root, 1)` ,将深度和节点编号按顺序记录到数组中,并记录各个点在 dfs 序列中第一次出现的位置。