POJ 1986 Distance Queries (Tarjan算法求最近公共祖先)
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;
}
POJ 1986 Distance Queries (Tarjan算法求最近公共祖先)的更多相关文章
- tarjan算法求最近公共祖先
tarjian算法 LCA: LCA(Least Common Ancestor),顾名思义,是指在一棵树中,距离两个点最近的两者的公共节点.也就是说,在两个点通往根的道路上,肯定会有公共的节点,我们 ...
- POJ 1986 Distance Queries / UESTC 256 Distance Queries / CJOJ 1129 【USACO】距离咨询(最近公共祖先)
POJ 1986 Distance Queries / UESTC 256 Distance Queries / CJOJ 1129 [USACO]距离咨询(最近公共祖先) Description F ...
- POJ.1986 Distance Queries ( LCA 倍增 )
POJ.1986 Distance Queries ( LCA 倍增 ) 题意分析 给出一个N个点,M条边的信息(u,v,w),表示树上u-v有一条边,边权为w,接下来有k个询问,每个询问为(a,b) ...
- POJ 1986 Distance Queries LCA两点距离树
标题来源:POJ 1986 Distance Queries 意甲冠军:给你一棵树 q第二次查询 每次你问两个点之间的距离 思路:对于2点 u v dis(u,v) = dis(root,u) + d ...
- POJ 1986 Distance Queries 【输入YY && LCA(Tarjan离线)】
任意门:http://poj.org/problem?id=1986 Distance Queries Time Limit: 2000MS Memory Limit: 30000K Total ...
- POJ 1986 Distance Queries (最近公共祖先,tarjan)
本题目输入格式同1984,这里的数据范围坑死我了!!!1984上的题目说边数m的范围40000,因为双向边,我开了80000+的大小,却RE.后来果断尝试下开了400000的大小,AC.题意:给出n个 ...
- POJ 1986 Distance Queries(Tarjan离线法求LCA)
Distance Queries Time Limit: 2000MS Memory Limit: 30000K Total Submissions: 12846 Accepted: 4552 ...
- POJ - 1986 Distance Queries(离线Tarjan算法)
1.一颗树中,给出a,b,求最近的距离.(我没考虑不联通的情况,即不是一颗树的情况) 2.用最近公共祖先来求, 记下根结点到任意一点的距离dis[],这样ans = dis[u] + dis[v] - ...
- POJ 1986 - Distance Queries - [LCA模板题][Tarjan-LCA算法]
题目链接:http://poj.org/problem?id=1986 Description Farmer John's cows refused to run in his marathon si ...
随机推荐
- ySQL性能优化的21个最佳实践 和 mysql使用索引
MySQL性能优化的21个最佳实践 和 mysql使用索引 今天,数据库的操作越来越成为整个应用的性能瓶颈了,这点对于Web应用尤其明显.关于数据库的性能,这并不只是DBA才需要担心的事,而这更是我 ...
- CentOS7无法使用tab补全功能??
Centos7在使用最小化安装的时候,没有安装自动补全的包,需要自己手动安装. yum -y install bash-completion 或者你可以安装一些初始化的包组 yum -y groupi ...
- JDBC数据库连接技术
[学习笔记]JDBC数据库连接技术(Java Database Connectivity) 一.JDBC简介 Java是通过JDBC技术实现对各种数据库的访问的,JDBC是Java数据库连接技术的简称 ...
- JAVA Junit4
JAVA Junit4 测试框架 序言 刚学Java的时候就听说过JUnit了,单元测试框架,很好用的测试框架,JUnit测试 ...
- Fortinet Security Fabric
Fortinet Security Fabric 这个世界从不固步自封.在技术方面,这意味着解决方案供应商必须保持不断创新和探索才能实现生存与发展. 在网络安全领域,这更是至理名言.许多黑客都是才华横 ...
- Counting
Description 数学老师走啦,英语老师来上课啦 他的性格与众不同,又因为大家都是理科班的学生 他希望大家在数字母的过程中领悟英语的快乐 他用m种字母进行排列组合, 得到了所 ...
- Moonraker:1靶机入侵
0x01 前言 攻击Moonraker系统并且找出存在最大的威胁漏洞,通过最大威胁漏洞攻击目标靶机系统并进行提权获取系统中root目录下的flag信息. Moonraker: 1镜像下载地址: h ...
- python抓取
我要抓取奥巴马每周的演讲内容http://www.putclub.com/html/radio/VOA/presidentspeech/index.html 如果手动提取,就需要一个个点进去,再复制保 ...
- 设计模式之单例模式实现(C++)
#ifndef SINGLETON_H #define SINGLETON_H #include <cassert> #include <memory> #include &l ...
- P2787 语文1(chin1)- 理理思维
P2787 语文1(chin1)- 理理思维 1.获取第x到第y个字符中字母k出现了多少次 2.将第x到第y个字符全部赋值为字母k 3.将第x到第y个字符按照A-Z的顺序排序 读字符串我再单个单个读我 ...