题目链接

Description

Farmer John's cows refused to run in his marathon since he chose a path much too long for their leisurely lifestyle. He therefore wants to find a path of a more reasonable length. The input to this problem consists of the same input as in "Navigation Nightmare",followed by a line containing a single integer K, followed by K "distance queries". Each distance query is a line of input containing two integers, giving the numbers of two farms between which FJ is interested in computing distance (measured in the length of the roads along the path between the two farms). Please answer FJ's distance queries as quickly as possible!

Input

  • Lines 1..1+M: Same format as "Navigation Nightmare"

  • Line 2+M: A single integer, K. 1 <= K <= 10,000

  • Lines 3+M..2+M+K: Each line corresponds to a distance query and contains the indices of two farms.

Output

  • Lines 1..K: For each distance query, output on a single line an integer giving the appropriate distance.

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

Hint

Farms 2 and 6 are 20+3+13=36 apart.

分析:

要求的是任意两个农场之间的距离,题目上虽然给出了路的方向,但是这对于解题来说没有影响,可以看作无向图。并且两个节点之间至多有1条路,根据数据建图一定是一颗树。所以我们可以先把任一节点作为根节点,用深搜获取每个节点到根节点的距离,然后用Tarjan算法求最近公共祖先以及两点间的距离。

具体实现时深搜求距离和Tarjan算法求最近公共祖先可以同时进行,由于储存点的时候既要方便遍历与某节点相邻的所有节点,又要存储边权,同时最好能直接通过下标来访问数据。一般的结构体或者vector数组就不好用了。这里采用了“前向星”的数据结构。

Tarjan算法:

对于新搜索到的一个节点,首先创建由这个节点构成的集合,再对当前结点的每个子树进行搜索,每搜索完一棵子树,则可以确定子树内的LCA询问都已解决。其他的LCA询问的结果必然在这个子树之外,这时把子树所形成的集合与前节点的集合合并,并将当前节点设为这个集合的祖先。之后继续索搜下一刻子树,直到当前节点的所有子树搜索完毕。这时把当前节点也设为已被检查过的,同时可以处理有关当前节点的lCA询问,如果有一个从当前节点到节点v的询问,且v已被检查过,则由于进行的是深度优先搜索,当前节点与v的最近公共祖先一定还没有检查,而这个最近公共祖先的包含v的子树一定搜索过了,那么这个最近公共祖先一定是v所在集合的祖先。

即对于每个节点u:

1.建立以u为代表元素的集合

2.遍历与u相连的节点v,如果没有被访问过,对于v使用Tarjan算法,结束后,将v的集合并入u的集合。

3.对于与u有关的询问(u,v),如果v被访问过,则结果就是v所在集合的代表元素。

递归求节点i到根节点的距离时,公式为:节点i对应起点到根节点的距离 + 起点到i的距离。对于所要求的两点间的距离公式为:两点到根节点的距离和 - 2 * 两点的最近公共祖先到根节点的距离。

代码:

#include<stdio.h>
#include<string.h>
#define MAX 80005 int id,iq; //分别记录存储的点和询问的个数
int f[MAX],vis[MAX],dis[MAX]; //dis[i]记录根节点到i的距离
//head[i]记录以i为起点的第一条边的下标,qhead类似
int head[MAX],qhead[MAX]; struct node
{ //前向星
int w; //两点间权值
int to; //终点
int next; //和to起点相同的下一条边的存储下标
} edge[MAX],que[MAX]; void add_edge(int u,int v,int w)
{ //加点
edge[id].to=v;
edge[id].w=w;
edge[id].next=head[u];
head[u]=id++;
edge[id].to=u;
edge[id].w=w;
edge[id].next=head[v];
head[v]=id++;
} void add_que(int u,int v)
{ //加询问
que[iq].to=v;
que[iq].next=qhead[u];
qhead[u]=iq++;
que[iq].to=u;
que[iq].next=qhead[v];
qhead[v]=iq++;
} void init(int n)
{ //并查集的初始化函数
int i;
for(i=0;i<=n;i++) f[i]=i;
memset(vis,0,sizeof(vis));
memset(dis,0,sizeof(dis));
memset(head,-1,sizeof(head)); //head初始化为-1,表示无下一条边
memset(qhead,-1,sizeof(qhead));
} int find(int x)
{ //并查集的压缩路径版查找函数
if(x!=f[x]) f[x]=find(f[x]);
return f[x];
} void Tarjan(int root)
{
int i;
vis[root]=1;
f[root]=root;
for(i=head[root];i!=-1;i=edge[i].next)
{ //不断搜索以当前顶点为起点的节点
if(!vis[edge[i].to])
{
//根节点到终点的距离=根节点到起点的距离 + 边权
dis[edge[i].to]=dis[root]+edge[i].w;
Tarjan(edge[i].to);
f[edge[i].to]=root;
}
}
for(i=qhead[root];i!=-1;i=que[i].next)
{ //查询和当前节点有关的询问
if(vis[que[i].to])
{
//两点间距离为两点到根节点的距离和 - 两倍的最近总共祖先到根节点距离
que[i].w=dis[root]+dis[que[i].to]-2*dis[find(que[i].to)];
que[i^1].w=que[i].w; //第i和i+1的结果相同(i为偶数)
}
}
} int main()
{
int i,t,n,m,k,u,v,w;
scanf("%d%d",&n,&m); //顶点数和边数
init(n);
id=0;
for(i=0;i<m;i++)
{
scanf("%d%d%d%*c%*c",&u,&v,&w); //两节点及其之间的权值
add_edge(u,v,w);
}
scanf("%d",&k); //询问数
iq=0;
for(i=0;i<k;i++)
{
scanf("%d%d",&u,&v);
add_que(u,v);
}
Tarjan(1); //选节点1作为根节点
for(i=0;i<iq;i+=2) printf("%d\n",que[i].w);
return 0;
}

最新文章

  1. Android应用底部导航栏(选项卡)实例
  2. javascript中的一些核心知识点以及需要注意的地方
  3. 10月28日PHP基础知识测试题
  4. 2016年湖南省第十二届大学生计算机程序设计竞赛---Parenthesis(线段树求区间最值)
  5. QT QDateTime类、QTimer类
  6. Jquery DataTables相关示例
  7. mysql is marked as crashed and should be repaired错误
  8. 【读书笔记】读《JavaScript设计模式》之适配器模式
  9. Matlab:拟合(1)
  10. wxPython缩放图片控件的一个小例子
  11. JVM -- 类加载
  12. poj 1185 火炮 (减少国家DP)
  13. vue2购物车ch4-(筛选v-for 点击的那个设置样式 设为默认地址其他 联动 非循环的列表选中和非选中 删除当前选中的列表)
  14. PHP中利用DOM和simplxml读取xml文档
  15. Java内存泄漏分析系列之一:使用jstack定位线程堆栈信息
  16. MySQL 库名重命名
  17. 数组实现int随机数的阶乘(避免大数问题)
  18. Entity Framework入门教程(13)---EF中的高并发
  19. js 去掉花括号
  20. sql-索引的作用

热门文章

  1. Window下Neo4j安装教程
  2. ci钩子
  3. Linux内核0.11 bootsect文件说明
  4. 【转载】Vue项目中的文件/文件夹命名规范
  5. [转帖]2018年JVM生态系统报告出炉
  6. 使用 Idea 打 scala程序的 jar 包 - 02
  7. linux中创建和解压文档的 tar 命令教程
  8. Windows安装ElastAlert问题总结
  9. 【Java】提取JSON数值时遇到数组集合时使用的K-V方式转换
  10. P4291 [HAOI2008]排名系统