LCA

LCA:一棵树上两个点向上最近的一个点,我们可以先对其中一个点向上标记至根节点,再对另外一个点向上标记,第一次遇到第一次标记的点就是所求。

此外,我们可以采用倍增的思想记录每个点向上2^k步的父节点,首先预处理出每个点的深度,以及每个点向上2^k步的父节点。

当我们查询两个点的LCA时,先把他们调整到同一深度,再看他们是否相当,不等就同时向上调整。

模板题:LCA

#include
using namespace std;

const int maxn = 50010;
int f[maxn][20], d[maxn], dist[maxn];
int ver[2 * maxn], Next[maxn * 2], edge[maxn * 2], head[maxn];
int T, n, m, tot, t;
queueq;

void add(int x, int y, int z) {
	ver[++tot] = y; edge[tot] = z; Next[tot] = head[x]; head[x] = tot;
}

void bfs() {
	q.push(1); d[1] = 1;
	while (!q.empty()) {
		int x = q.front(); q.pop();
		for (int i = head[x]; i; i = Next[i]) {
			int y = ver[i];
			if (d[y])continue;
			d[y] = d[x] + 1;
			dist[y] = dist[x] + edge[i];
			f[y][0] = x;
			for (int j = 1; j <= t; j++)
				f[y][j] = f[f[y][j - 1]][j - 1];
			q.push(y);
		}
	}
}

int lca(int x, int y) {
	if (d[x] < d[y])swap(x,y);
	for (int i = t; i >= 0; i--) {
		if (d[f[x][i]] >= d[y])x = f[x][i];
	}
	if (x == y)return x;
	for (int i = t; i >= 0; i--) {
		if (f[x][i] != f[y][i])x = f[x][i], y = f[y][i];
	}
	return f[x][0];
}

int main() {
	cin >> T;
	while (T--) {
		cin >> n >> m;
		t = (int)(log(n) / log(2)) + 1;
		memset(head,0,sizeof(0));
		memset(d,0,sizeof(0));
		tot = 0;
		for (int i = 1; i < n; i++) {
			int x, y, z;
			scanf("%d%d%dd",&x,&y,&z);
			add(x,y,z);
			add(y,x,z);
		}
		bfs();
		for (int i = 1; i <= m; i++) {
			int x, y;
			scanf("%d%d",&x,&y);
			printf("%d\n",dist[x]+dist[y]-2*dist[lca(x,y)]);
		}
	}

	return 0;
}

 

Tarjan算法:

           这是一个离线的求LCA的算法,先把所有要求的点存起来,然后在树上跑一遍dfs,并且标记哪些点是没有访问过的,哪些是已经访问过的,哪些已经访问完毕且回溯了;

#include
using namespace std;

#define ll long long
const int maxn = 50010;
int ver[maxn * 2], Next[maxn * 2], edge[maxn * 2], head[maxn];
int fa[maxn], d[maxn], v[maxn], lca[maxn], ans[maxn];
vector query[maxn], query_id[maxn];
int T, n, m, tot, t;
void add(int x, int y, int z) {
	ver[++tot], edge[tot] = z, Next[tot] = head[x], ver[tot] = y; head[x] = tot;
}

void add_query(int x, int y, int id) {
	query[x].push_back(y),query_id[x].push_back(id);
	query[y].push_back(x),query_id[y].push_back(id);
}

int get(int x) {
	return fa[x] == x ? x : fa[x] = get(fa[x]);
}

void tarjan(int x) {
	v[x] = 1;
	for (int i = head[x]; i; i = Next[i]) {
		int y = ver[i];
		if (v[y])continue;
		d[y] = d[x] + edge[i];
		tarjan(y);
		fa[y] = x;
	}
	for (int i = 0; i < query[x].size(); i++) {
		int y = query[x][i], id = query_id[x][i];
		if (v[y] == 2) {
			int lca = get(y);
			ans[id] = min(ans[id],d[x]+d[y]-2*d[lca]);
		}
	}
	v[x] = 2;
}

int main() {
	cin >> T;
	while (T--) {
		cin >> n >> m;
		for (int i = 1; i <= n; i++) {
			head[i] = 0, fa[i] = i, v[i] = 0;
			query[i].clear();
			query_id[i].clear();
		}
		tot = 0;

		for (int i = 1; i < n; i++) {
			int x, y, z;
			scanf("%d%d%d",&x,&y,&z);
			add(x,y,z);
			add(y,x,z);
		}

		for (int i = 1; i <= m; i++) {
			int x, y;
			scanf("%d%d",&x,&y);
			if (x == y)ans[i] = 0;
			else {
				add_query(x,y,i);
				ans[i] = 1 << 30;
			}
		}

		tarjan(1);
		for (int i = 1; i <= m; i++)
			printf("%d\n",ans[i]);
	}

	return 0;
} 

 

你可能感兴趣的:(图论,LCA,倍增)