Connections between cities(LCA倍增法)

本文介绍如何使用并查集判断图中两点是否联通,并通过LCA算法计算两点间距离。采用倍增法实现LCA,适用于多组输入数据处理。

题意:
给你n个点,m条边,进行c此询问,若询问的两点是联通的,则输出两者的距离,否则输出Not connected。
首先判断是否连通,显然用并查集就可以愉快的解决。对于剩下的部分,这道题也算模板题吧(然而本蒟蒻WA了8次doge),用dis数组记录节点到根节点的距离,那么两者间的距离不就等于dis[u]+dis[v]-2*dis[lca(u,v)]了吗?
上边说过了这道题算是一道模板题,所以也没什么巧妙的地方要讲,那么就这么愉快地结束分析部分吧。我是用的倍增法,如果你不会LCA。。emmm,LCA,点击就送.

下面上代码吧

#include<cstdio>
#include<algorithm>
#include<cstring>

using namespace std;

const int maxn = 10020;

int head[maxn];
int depth[maxn];
int ls[maxn][20];
int pre[maxn];
int dis[maxn];
int n, m, c, cnt;

struct node {
	int to;
	int nxt;
	int val;
}date[maxn<<1];

int find(int x) {//愉快地并查集
	if (pre[x] == x) return x;
	pre[x] = find(pre[x]);
	return pre[x];
}

void merge(int u, int v) {//愉快的合并
	int fu = find(u);
	int fv = find(v);
	if (fu != fv) {
		pre[fu] = fv;
	}
}

void addedge(int u, int v, int w) {//愉快地建边
	date[++cnt] = node{ v,head[u],w };
	head[u] = cnt;
}

void init_num() {//开始懒得写那么沙雕的东西
	for (int i = 1; i <= n; i++) {
		pre[i] = i;
	}
	memset(head, -1, sizeof(head));
	memset(ls, 0, sizeof(ls));
	memset(dis, 0, sizeof(dis));
	memset(depth, 0, sizeof(depth));
	cnt = 0;
}

void dfs(int x, int p) {
	ls[x][0] = p;
	depth[x] = depth[p] + 1;
	for (int i = 1; i <= 19; i++) { 
		ls[x][i] = ls[ls[x][i - 1]][i - 1];
	}	
	for (int i = head[x]; ~i; i = date[i].nxt) { 
		int v = date[i].to;	
		if (v != p){
			dis[v] = dis[x] + date[i].val;
			dfs(v, x);
		} 
	} 
}


int lca(int x, int y) {
	if (depth[x] < depth[y]) {
		swap(x, y);
	}
	for (int i = 19; i >= 0; i--) {//扔到同一层
		if (depth[ls[x][i]]>=depth[y]) {
			x = ls[x][i];
		}
	}
	if (x == y) return x;
	for (int i = 19; i >= 0; i--) {
		if (ls[x][i] != ls[y][i]) {
			x = ls[x][i];
			y = ls[y][i];
		}
	}
	return ls[x][0];
}


int main() {
	while (scanf("%d %d %d", &n, &m, &c) != EOF) {//注意此题多组输入,我没多组输入WA了六次,卡了一天(微笑中透露疲惫.jpg)
		init_num();
		for (int i = 1; i <= m; i++) {
			int u, v, w;
			scanf("%d %d %d", &u, &v, &w);
			addedge(u, v, w);
			addedge(v, u, w);
			merge(u, v);
		}
		for (int i = 1; i <= n; i++) {
			if (pre[i]==i) {
				dfs(i, 0);
			}
		}
		while (c--) {
			int u, v;
			scanf("%d %d", &u, &v);
			if (find(u) != find(v)) {
				printf("Not connected\n");
			}
			else {
				printf("%d\n", dis[u] + dis[v] - 2 * dis[lca(u, v)]);
			}
		}
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值