LCA

开题之前先回顾一遍链表的基本操作:

struct Node
{
	int value;
	Node * prev,* Next;
};
Node * head ,* tail;
void initialize(){//新建链表
	head = new Node();
	tail = new Node();
	head -> Next = tail;
	tail -> prev = head;
}
void insert(Node *p,int val){//在p节点后插入包括数据为val的节点
	q = new Node();
	q -> value = val;
	p ->Next -> prev = q;
	q -> Next = p ->Next;
	p -> Next = q;
	q -> prev = p;
}
void remove(Node *p){//删除p节点
	p -> prev -> Next = p-> Next;
	p -> Next -> prev = p ->prev;
	delete p;
}
void recycle(){//链表内存回收
	while(head != tail){
		head = head ->Next;
		delete head ->prev;
	}
}

分割线--------------------------------------------------------分割线
以下是邻接链表的操作

const int maxn = 1e5+7;
int tot,edge[maxn*2],ver[maxn],next[maxn],head[maxn];
void add(int x ,int y ,int z){//加入有向边(x,y),权值为z
	ver[++tot] = y,edge[tot] = z;
	next[tot] = head[x], head[x] = tot;
}
void find(){
	for(int i = head[x];i;i = Next[i]){
		int y = ver[i] , z = edge[i];
		//找到一条有向边(x,y),权值为z
	}
}

分割线--------------------------------------------------------分割线
引入问题:
定义:LCA(Lowest Common Ancestors),即最近公共祖先,是指在有根树中,找出某两个结点u和v最近的公共祖先。
在这里插入图片描述
一般的做法,先求出每个点的深度,一个点一个点往上跳,等跳到同一个点结束,这样是可行的,但是我们有更好的方法-----倍增法;
在这里插入图片描述
倍增法顾名思义,就是跳的步数加倍,一开始从2^i 开始跳,i >= log2(点的个数)+1,下一次的跳的步数是前一次的一半,跳到同一个位置就跳,不是一个位置就不跳,但是最后的结果要往上走一步才是LCA;
在这里插入图片描述
分割线--------------------------------------------------------分割线

用bfs预处理出各个点的深度;f[x][k]为x点的2^k辈祖先,有以下递推式:
	f[x][k] = f[f[x][k-1]][k-1], 1<=k<=logn;
void bfs(){//预处理
	q.push(1),d[1] = 1;
	while(q.size()){
		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[y];
			f[y][0] = x;
			for(int j= 1 ; j<=t ;j++){
				f[y][j] = f[f[y][j-1]][j-1];
			}
			q.psuh(y);
		}
	}
}
int lca(int x ,int y){//处理单次查询
	if(d[y] > d[x])swap(x,y);
	for(int i = t;i >= 0;i--){
		if(d[f[y][i]] >= d[x]) y = d[y][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];
}

分割线--------------------------------------------------------分割线
HDU - 2586

#include<bits/stdc++.h>
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;
queue<int>q;
 
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;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值