[HDU2874]Connections between cities

题目大意:给你n个节点的森林(注意不是一棵树),m条路径的长度,c个询问,要你回答每个询问两个点i和j的最短距离,或者回答没有连接。

解题思路:对于每个节点,求出其到根的距离a[i],然后求两点的最近公共祖先LCA,最后的答案为$a[i]+a[j]-2*a[LCA(i,j)]$。

我用的是Tarjan算法。

注意此题可能出现两个相同点的情况,需要注意一些细节,或者特判。

C++ Code:

 

#include<cstdio>
#include<cstring>
using namespace std;
int n,m,c,cnt=0,cntq=0,head[10002],headq[1000002],fa[10002],a[10002]={0};
bool vis[10002],all[10002];
struct edge{
	int to,dist,next;
}e[20004];
struct Q{
	int to,next;
}q[2000004];
int ans[1000002];
void addedge(int from,int to,int dist){
	cnt++;
	e[cnt].to=to;
	e[cnt].dist=dist;
	e[cnt].next=head[from];
	head[from]=cnt;
	cnt++;
	e[cnt].to=from;
	e[cnt].dist=dist;
	e[cnt].next=head[to];
	head[to]=cnt;
}
int dad(int x){
	return(fa[x]==x)?(x):(fa[x]=dad(fa[x]));
}
void addq(int x,int y){
	cntq++;
	q[cntq].to=y;
	q[cntq].next=headq[x];
	headq[x]=cntq;
	cntq++;
	q[cntq].to=x;
	q[cntq].next=headq[y];
	headq[y]=cntq;
}
void lca(int root,int pre){
	fa[root]=root;
	for(int i=head[root];i;i=e[i].next){
		int p=e[i].to;
		if(p!=pre){
			a[p]=a[root]+e[i].dist;
			lca(p,root);
		}
	}
	vis[root]=all[root]=true;
	for(int i=headq[root];i;i=q[i].next){
		int p=q[i].to;
		if(vis[p]){
			int LCA=dad(p);
			ans[(i+1)/2]=a[root]+a[p]-a[LCA]*2;
		}
	}
	fa[root]=pre;
}
int main(){
	while(scanf("%d%d%d",&n,&m,&c)!=EOF){
		cnt=cntq=0;
		memset(a,0,sizeof(a));
		memset(fa,0,sizeof(fa));
		memset(head,0,sizeof(head));
		memset(headq,0,sizeof(headq));
		memset(e,0,sizeof(e));
		memset(q,0,sizeof(q));
		memset(ans,-1,sizeof(ans));
		memset(all,0,sizeof(all));
		for(int i=1;i<=m;i++){
			int from,to,dist;
			scanf("%d%d%d",&from,&to,&dist);
			addedge(from,to,dist);
		}
		for(int i=1;i<=c;i++){
			int x,y;
			scanf("%d%d",&x,&y);
			addq(x,y);
		}
		for(int i=1;i<=n;i++)
		if(!all[i]){
			memset(vis,0,sizeof(vis));
			lca(i,0);
		}
		for(int i=1;i<=c;i++)
		if(ans[i]==-1)puts("Not connected");else
		printf("%d
",ans[i]);
	}
	return 0;
}
原文地址:https://www.cnblogs.com/Mrsrz/p/7147259.html