hdu 2874 Connections between cities(LCA)

Connections between cities

 

Time Limit: 10000/5000 MS(Java/Others)    Memory Limit:32768/32768 K (Java/Others)

Total Submission(s): 3900    Accepted Submission(s): 1134

 

 

Problem Description

After World War X, a lot of cities havebeen seriously damaged, and we need to rebuild those cities. However, somematerials needed can only be produced in certain places. So we need totransport these materials from city to city. For most of roads had been totallydestroyed during the war, there might be no path between two cities, no circleexists as well.

Now, your task comes. After giving you thecondition of the roads, we want to know if there exists a path between any twocities. If the answer is yes, output the shortest path between them.

 

 

Input

Input consists of multiple probleminstances.For each instance, first line contains three integers n, m and c,2<=n<=10000, 0<=m<10000, 1<=c<=1000000. n represents thenumber of cities numbered from 1 to n. Following m lines, each line has threeintegers 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 cityj.

 

 

Output

For each problem instance, one line foreach query. If no path between two cities, output “Not connected”, otherwiseoutput 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

 

Hint

 

Hint

 

Huge input, scanf recommended.

 

题解:

这道题用的是LCA离线算法,LCA离线算法是从结点1出发,找到1的子结点,计算出1到子节点的距离,然后dfs子节点的子结点,算出子孙结点到根结点的距离,dis[v] = dis[u]+edge[k].v;在记录完当前结点所有子节点到祖先的距离后,判断是否有询问以当前节点为出发点问题,如另一个点的距离遍历过了,如果遍历过了,则另一个结点的祖先节点就是公共节点,距离等于query[k].v = dis[u]+dis[v]-dis[tp]*2;如果两个点不联通,可通过查找祖先,如果祖先不相同,则不再同一棵树上。

源代码:

#include <iostream>

#include <stdio.h>

#include <string.h>

#include <vector>

#define MAX 20050

using namespace std;

struct edge

{

       intfr;

       intto,v,next;

}ed[MAX],query[2000005];

int n,m;

bool visit[MAX];

//dis表示到当前结点到最远祖先的距离。

int dis[10050],parent[10050];

int e[10050],q[10050];//e存储着i点在ed中的位置(如果是跟该e会发生变化)

//q存储着i点在query中的位置;

 

int find_parent(int x)

{

       if(parent[x]== x)

              returnx;

       returnparent[x] = find_parent(parent[x]);

}

 

void dfs(int u)

{

       visit[u]= true;

       intv;

       for(intk = e[u];k != -1;k = ed[k].next)

       {

              v= ed[k].to;

              //u是根节点,dis[v]表示v点到根节点的距离

              if(!visit[v])

              {

                     dis[v]= dis[u]+ed[k].v;

                     dfs(v);

                     parent[v]= u;

              }

       }

       inttp;

       //u是根节点,下面的循环是计算含有u点的路径值

       for(intk = q[u];k != -1;k = query[k].next)

       {

              v= query[k].to;

              //u是当前结点,v点如果已经遍历过了,

              //若u和v有相同祖先,则tp表示最近祖先。

              //若u.v没有相同祖先,则计算的值无效,

              //在main函数会进行判断两个点是否在同一颗树下;

              if(visit[v])

              {

                     tp= find_parent(v);

                     query[k].v= dis[u] + dis[v] - dis[tp]*2;

                     query[k^1].v= query[k].v;

                     //k^1,^表示异或,即k的二进制与00...1异或,

                     //如果尾数为1则变成0,如果尾数为0,则变成1

              }

       }

}

 

int main()

{

       intqu;

       intne,nq;

       while(scanf("%d%d%d",&n,&m,&qu)!= EOF)

       {

              ne= 0;

              for(inti = 1;i <= n;i++)

              {

                     parent[i]= i;

                     e[i]= -1;

                     q[i]= -1;

                     visit[i]= false;

              }

              intc1,c2,v;

              for(inti = 0;i < m;i++)

              {

                     scanf("%d%d%d",&c1,&c2,&v);

                     ed[ne].next=e[c1];

                     ed[ne].to= c2;

                     ed[ne].v= v;

                     e[c1]= ne++;

                     ed[ne].next= e[c2];

                     ed[ne].to= c1;

                     ed[ne].v= v;

                     e[c2]= ne++;

              }

              nq= 0;

              for(inti = 0;i < qu;i++)

              {

                     scanf("%d%d",&c1,&c2);

                     query[nq].fr= c1;

                     query[nq].to= c2;

                     query[nq].v= -1;

                     query[nq].next= q[c1];

                     q[c1]= nq++;

                     query[nq].fr= c2;

                     query[nq].next= q[c2];

                     query[nq].to= c1;

                     query[nq].v= -1;

                     q[c2]= nq++;

              }

 

              //dfs中每一个进入的点都会计算出其子树中存在的路径

              for(inti = 1;i <= n;i++)

                     //所有与1相连或与其子辈相连的点都会成为1的子辈,

                     //不与1相连的点会在另一颗树上

                     if(!visit[i])

                            dfs(i);

 

              for(inti = 0;i < nq;i+=2)

              {

                     intj = query[i].fr;

                     intk =  query[i].to;

                     if(find_parent(j)!= find_parent(k))

                            printf("Notconnected\n");

                     else

                            printf("%d\n",query[i].v);

              }

       }

       return0;

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值