题目链接:
Connections between cities
Time Limit: 10000/5000 MS (Java/Others)
Memory Limit: 32768/32768 K (Java/Others)
Problem Description
After World War X, a lot of cities have been seriously damaged, and we need to rebuild those cities. However, some materials needed can only be produced in certain places. So we need to transport these materials from city to city. For most of roads had been totally destroyed during the war, there might be no path between two cities, no circle exists as well.
Now, your task comes. After giving you the condition of the roads, we want to know if there exists a path between any two cities. If the answer is yes, output the shortest path between them.
Input
Input consists of multiple problem instances.For each instance, first line contains three integers n, m and c, 2<=n<=10000, 0<=m<10000, 1<=c<=1000000. n represents the number of cities numbered from 1 to n. Following m lines, each line has three integers i, j and k, represent a road between city i and city j, with length k. Last c lines, two integers i, j each line, indicates a query of city i and city j.
Output
For each problem instance, one line for each query. If no path between two cities, output “Not connected”, otherwise output the length of the shortest path between them.
Sample Input
5 3 2
1 3 2
2 4 3
5 2 3
1 4
4 5
Sample Output
Not connected
6
题意:
给了一些边,可能是一棵树,可能是森林,c个询问,问i和j之间的距离;
思路:
由于是森林,所以采用lca的离线算法Tarjan;开两个并查集,一个可以先预处理是否在一棵树上,另一个用在Tarjan上;
Tarjan算法的思想是一边处理,一边回答询问,把一个节点的所有子树处理完后就可以回答完关于这个节点的和所有已经处理完点的询问了;
AC代码:
/* 2874 1731MS 29448K 2286 B G++ 2014300227 */ #include <bits/stdc++.h> using namespace std; const int N=1e4+4; typedef long long ll; const double PI=acos(-1.0); int n,m,c,cnt,head[N],a,b,num,pre[N],p[N],l,r,v,dis[N],ans[1000005],vis[N],fa[N]; struct Edge { int to,va,next; }; Edge edge[2*N]; struct ques { int to,next,id; }; ques que[2000000+20]; void add(int s,int e,int v) { //edge[cnt].fr = s; edge[cnt].to = e; edge[cnt].va=v; edge[cnt].next = head[s]; head[s] = cnt++;//学会了这种存边的方法; } void q_add(int s,int e,int order) { //que[num].fr = s; que[num].to = e; que[num].next = pre[s]; que[num].id=order; pre[s] = num++; } int findset(int x) { if(x == p[x])return x; return p[x] = findset(p[x]); } int fun(int x) { if(x==fa[x])return x; return fa[x]=fun(fa[x]); } void Tarjan(int x,int dist) { vis[x] = 1; dis[x]=dist; for(int i = head[x];i!=-1;i = edge[i].next)//head[x]指向以x为端点的一条边;下面的pre[x]也是相同的道理; { int y = edge[i].to; if(!vis[y]) { Tarjan(y,dist+edge[i].va); fa[y] = x; } }//前边表示这个节点的所有子树已经处理完毕,下面可以回答相关的询问了; for(int i = pre[x];i!=-1;i = que[i].next) { int y = que[i].to; if(findset(x) == findset(y)) { if(vis[y]) ans[que[i].id] = dis[y]+dis[x]-2*dis[fun(y)]; } else ans[que[i].id] = -1; } } int main() { while(scanf("%d%d%d",&n,&m,&c)!=EOF) { for(int i = 0;i <= n;i++) { p[i] = i; fa[i] = i; head[i]=pre[i]=-1; vis[i]=0; } cnt = 0; num = 0; for(int i = 0;i < m;i++) { scanf("%d%d%d",&l,&r,&v); int fx=findset(l),fy=findset(r); if(fx!=fy)p[fx]=fy;//看是否在一个树上的并查集 add(l,r,v); add(r,l,v); } for(int i = 0;i < c;i++) { scanf("%d%d",&a,&b); q_add(a,b,i); q_add(b,a,i); } for(int i=1;i<=n;i++) { if(!vis[i]) { Tarjan(i,0); } } for(int i = 0;i < c;i++) { if(ans[i]>-1)printf("%d\n",ans[i]); else printf("Not connected\n"); } } return 0; }