POJ 1986 Tarjan离线算法(最近公共祖先)

题意就是求两个点之间的最短距离 那个字母是没有用的

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<vector>
using namespace std;
const int N=40010;
vector<int> Q[N],num[N];//定义容器 Q[0]为一个容器 Q[1]为一个容器以此类推 每个容器都类似于一个数组 只是需要多大能开多大 节约内存(大概是这样我也不是很懂) 
int fa[N],d[N],LCA[N],qx[N],qy[N],n,m,g;
int Begin[N],Next[N*2],to[N*2],w[N*2],e;
void Add(int x,int y,int k){//链式前向星(这个知道的应该很多吧)to[i]是第i条边的目标点 w[i]是第i条边的长度 next[i]指与第i条边的起始点相同的下一条边的编号 begin[j]指输入的最后一条以j为起始点的边的编号即从j点开始遍历的第一条边
	to[++e]=y;
	w[e]=k;
	Next[e]=Begin[x];//至于最后两行为什么这样写 画个图就知道了很好理解 
	Begin[x]=e;
}//链接矩阵需要n*n的数组 链接表需要n*m的数组 前向星只需要几个m的一维数组 最节约内存 
void Addq(int x, int y, int k){
	Q[x].push_back(y);//压入 
	num[x].push_back(k);//Q[x]是以x为终点的询问的起点的集合 num[x]是以x为起点的询问的编号的集合 
}
int find(int x){
	return x==fa[x]?x:(fa[x]=find(fa[x]));
}
void Tarjan(int h, int dis){
	fa[h]=h;//每个节点的父亲节点设为自己 
	d[h]=dis;//d[h]代表从h点到根节点即到1点的距离 
	for(int i=Begin[h];i;i=Next[i]){//链式前向星的应用 终止条件为编号变成0即后面没边了 
		int v=to[i];
		if(!fa[v]){
			Tarjan(v,dis+w[i]);
			fa[v]=h;//回溯完再将这个点的父亲节点设置为父亲 自己模拟一下就知道为什么了
		}
	}
	for(int i=0;i<=(int)Q[h].size()-1;i++){//因为h点正在遍历 所以只要询问中有一点为h点并且另一点已经遍历过就可以找他们的最近公共祖先了 
		int v = Q[h][i];
		if(fa[v])
			LCA[num[h][i]]=find(v);//LCA[i]指第i个询问(注意是询问)的两个点的最近公共祖先 
	}
}
int main(){
	scanf("%d%d",&n,&m);
	for(int i=1;i<=m;i++){
		int x,y,z;
		scanf("%d%d%d%c%c",&x,&y,&z);
		Add(x,y,z);
		Add(y,x,z);
	}
	scanf("%d",&g);
	for(int i=1;i<=g;i++){
		scanf("%d%d",&qx[i],&qy[i]);
		Addq(qx[i],qy[i],i);
		Addq(qy[i],qx[i],i);
	}
	Tarjan(1,0);//从根节点开始遍历 1是根节点的编号 0是离根节点的距离 过程类似于dfs 
	for(int i=1;i<=g;i++)
		printf("%d\n",d[qx[i]]+d[qy[i]]-2*d[LCA[i]]);;
	return 0;
}


Sample Input

7 6
1 6 13 E
6 3 9 E
3 5 7 S
4 1 3 N
2 4 20 W
4 7 2 S
3
1 6
1 4
2 6

Sample Output


13
3
36
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值