题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2586

Problem Description
There are n houses in the village and some bidirectional roads connecting them. Every day peole always like to ask like this "How far is it if I want to go from house A to house B"? Usually it hard to answer. But luckily int this village the answer is always
unique, since the roads are built in the way that there is a unique simple path("simple" means you can't visit a place twice) between every two houses. Yout task is to answer all these curious people.
 
Input
First line is a single integer T(T<=10), indicating the number of test cases.

  For each test case,in the first line there are two numbers n(2<=n<=40000) and m (1<=m<=200),the number of houses and the number of queries. The following n-1 lines each consisting three numbers i,j,k, separated bu a single space, meaning that there is a road
connecting house i and house j,with length k(0<k<=40000).The houses are labeled from 1 to n.

  Next m lines each has distinct integers i and j, you areato answer the distance between house i and house j.
 
Output
For each test case,output m lines. Each line represents the answer of the query. Output a bland line after each test case.
 
Sample Input
2
3 2
1 2 10
3 1 15
1 2
2 3 2 2
1 2 100
1 2
2 1
 
Sample Output
10
25
100
100
 
Source

题意:

一个村庄有 n 个房子和 n-1 条双向路,每两个房子之间都有一条简单路径。

如今有m次询问。求两房子之间的距离。

PS:

能够用LCA来解,首先找到u, v 两点的lca,然后计算一下距离值就能够了。

计算方法是。记下根结点到随意一点的距离dis[i],

这样ans = dis[u] + dis[v] - 2 * dis[lca(v, v)]了。

这题要用c++交。G++会爆栈!

代码例如以下:看别人的模板(tarjan 离线)

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
#define maxn 40047
#define maxm 247 struct node
{
int to,w,next;
} edge[maxn*2]; int n, m; //点数,询问次数
int head[maxn];
int k;
int fa[maxn]; //父亲结点
int dis[maxn]; //到根节点距离
int vis[maxn]; //是否訪问过
int s[maxm]; //询问起点
int e[maxm]; //询问终点
int lca[maxm]; //LCA(s,e) 近期公共祖先 int find(int x)
{
if(fa[x]!=x) return fa[x]=find(fa[x]);
return fa[x];
} void init()
{
k = 1;
memset(head,0,sizeof(head));
memset(dis,0,sizeof(dis));
memset(vis,0,sizeof(vis));
} void add(int u,int v,int w)
{
edge[k].to = v;
edge[k].w = w;
edge[k].next = head[u];
head[u] = k++;
} void tarjan(int u)
{
int i,v;
fa[u] = u;
vis[u] = 1;
for(i = 0; i < m; i++)
{
if(e[i]==u && vis[s[i]])
lca[i] = find(s[i]); //若询问的两点中有一点已被訪问过。则两点的LCA则为这一点的当前父节点
if(s[i]==u && vis[e[i]])
lca[i] = find(e[i]);
}
for(i = head[u]; i; i = edge[i].next)
{
v = edge[i].to;
if(!vis[v]) //若没被訪问过
{
dis[v] = dis[u]+edge[i].w;//更新距离
tarjan(v);
fa[v] = u;//回溯更新父节点
}
}
} int main()
{
int t;
scanf("%d",&t);
while(t--)
{
init();
scanf("%d%d",&n,&m);
int u, v, w;
for(int i = 0; i < n-1; i++)
{
scanf("%d%d%d",&u,&v,&w);
add(u,v,w);
add(v,u,w);
}
for(int i = 0; i < m; i++)
{
scanf("%d%d",&s[i],&e[i]);
}
tarjan(1); for(int i = 0; i < m; i++)
{
printf("%d\n",dis[s[i]]+dis[e[i]]-2*dis[lca[i]]);//两点距离为根节点到两点距离之和-根节点到LCA距离*2
}
}
return 0;
}

(ST在线算法 转)

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
using namespace std;
//#pragma comment(linker, "/STACK:102400000,102400000") //不须要申请系统栈
const int N = 40010;
const int M = 25;
int dp[2*N][M]; //这个数组记得开到2*N,由于遍历后序列长度为2*n-1
bool vis[N];
struct edge
{
int u,v,w,next;
} e[2*N];
int tot,head[N];
inline void add(int u ,int v ,int w ,int &k)
{
e[k].u = u;
e[k].v = v;
e[k].w = w;
e[k].next = head[u];
head[u] = k++;
u = u^v;
v = u^v;
u = u^v;
e[k].u = u;
e[k].v = v;
e[k].w = w;
e[k].next = head[u];
head[u] = k++;
}
int ver[2*N],R[2*N],first[N],dir[N];
//ver:节点编号 R:深度 first:点编号位置 dir:距离
void dfs(int u ,int dep)
{
vis[u] = true;
ver[++tot] = u;
first[u] = tot;
R[tot] = dep;
for(int k=head[u]; k!=-1; k=e[k].next)
if( !vis[e[k].v] )
{
int v = e[k].v , w = e[k].w;
dir[v] = dir[u] + w;
dfs(v,dep+1);
ver[++tot] = u;
R[tot] = dep;
}
}
void ST(int n)
{
for(int i=1; i<=n; i++)
dp[i][0] = i;
for(int j=1; (1<<j)<=n; j++)
{
for(int i=1; i+(1<<j)-1<=n; i++)
{
int a = dp[i][j-1] , b = dp[i+(1<<(j-1))][j-1];
dp[i][j] = R[a]<R[b]?a:b;
}
}
}
//中间部分是交叉的。
int RMQ(int l,int r)
{
int k=0;
while((1<<(k+1))<=r-l+1)
k++;
int a = dp[l][k], b = dp[r-(1<<k)+1][k]; //保存的是编号
return R[a]<R[b]?a:b;
} int LCA(int u ,int v)
{
int x = first[u] , y = first[v];
if(x > y) swap(x,y);
int res = RMQ(x,y);
return ver[res];
} int main()
{
//freopen("Input.txt","r",stdin);
//freopen("Out.txt","w",stdout);
int cas;
scanf("%d",&cas);
while(cas--)
{
int n,q,num = 0;
scanf("%d%d",&n,&q);
memset(head,-1,sizeof(head));
memset(vis,false,sizeof(vis));
for(int i=1; i<n; i++)
{
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
add(u,v,w,num);
}
tot = 0;
dir[1] = 0;
dfs(1,1);
/*printf("节点ver "); for(int i=1; i<=2*n-1; i++) printf("%d ",ver[i]); cout << endl;
printf("深度R "); for(int i=1; i<=2*n-1; i++) printf("%d ",R[i]); cout << endl;
printf("首位first "); for(int i=1; i<=n; i++) printf("%d ",first[i]); cout << endl;
printf("距离dir "); for(int i=1; i<=n; i++) printf("%d ",dir[i]); cout << endl;*/
ST(2*n-1);
while(q--)
{
int u,v;
scanf("%d%d",&u,&v);
int lca = LCA(u,v);
printf("%d\n",dir[u] + dir[v] - 2*dir[lca]);
}
}
return 0;
}

HDU 2586 How far away ?(LCA模板 近期公共祖先啊)的更多相关文章

  1. LCA模板 ( 最近公共祖先 )

    LCA 有几种经典的求取方法.这里只给出模板,至于原理我完全不懂. 1.RMQ转LCA.复杂度O(n+nlog2n+m) 大致就是 DFS求出欧拉序 => 对欧拉序做ST表 => LCA( ...

  2. UVA - 11354Bond最小生成树,LCA寻找近期公共祖先

    看懂题目意思.他的意思是求将全部的城市走一遍,危急度最小.而且给 你两个s,t后让你求在走的时候,从s到t过程中危急度最大的值,并输出它, 然后就是怎样攻克了,这个题目能够说简单,也能够说难 通过思考 ...

  3. LintCode 近期公共祖先

    中等 近期公共祖先 查看执行结果 34% 通过 给定一棵二叉树,找到两个节点的近期公共父节点(LCA). 近期公共祖先是两个节点的公共的祖先节点且具有最大深度. 您在真实的面试中是否遇到过这个题? Y ...

  4. 连通分量模板:tarjan: 求割点 &amp;&amp; 桥 &amp;&amp; 缩点 &amp;&amp; 强连通分量 &amp;&amp; 双连通分量 &amp;&amp; LCA(近期公共祖先)

    PS:摘自一不知名的来自大神. 1.割点:若删掉某点后.原连通图分裂为多个子图.则称该点为割点. 2.割点集合:在一个无向连通图中,假设有一个顶点集合,删除这个顶点集合,以及这个集合中全部顶点相关联的 ...

  5. LCA近期公共祖先

    LCA近期公共祖先 该分析转之:http://kmplayer.iteye.com/blog/604518 1,并查集+dfs 对整个树进行深度优先遍历.并在遍历的过程中不断地把一些眼下可能查询到的而 ...

  6. POJ 1470 Closest Common Ancestors【近期公共祖先LCA】

    版权声明:本文为博主原创文章,未经博主同意不得转载. https://blog.csdn.net/u013912596/article/details/35311489 题目链接:http://poj ...

  7. 近期公共祖先(LCA)——离线Tarjan算法+并查集优化

    一. 离线Tarjan算法 LCA问题(lowest common ancestors):在一个有根树T中.两个节点和 e&sig=3136f1d5fcf75709d9ac882bd8cfe0 ...

  8. LCA 近期公共祖先 小结

    LCA 近期公共祖先 小结 以poj 1330为例.对LCA的3种经常使用的算法进行介绍,分别为 1. 离线tarjan 2. 基于倍增法的LCA 3. 基于RMQ的LCA 1. 离线tarjan / ...

  9. POJ1330Nearest Common Ancestors——近期公共祖先(离线Tarjan)

    http://poj.org/problem? id=1330 给一个有根树,一个查询节点(u,v)的近期公共祖先 836K 16MS #include<iostream> #includ ...

随机推荐

  1. Gym - 101981D The 2018 ICPC Asia Nanjing Regional Contest D.Country Meow 最小球覆盖

    题面 题意:给你100个三维空间里的点,让你求一个点,使得他到所有点距离最大的值最小,也就是让你找一个最小的球覆盖掉这n个点 题解:红书模板题,这题也因为数据小,精度也不高,所以也可以用随机算法,模拟 ...

  2. ROS-URDF-Xacro

    前言:Xacro是一种宏语言,允许代码复用,使用Xacro可以减少URDF文件中的代码量. 参考自:http://wiki.ros.org/urdf/Tutorials/Using%20Xacro%2 ...

  3. BZOJ 4565 状压DP

    思路: f[i][j][S]表示从i到j压成S状态 j-m是k-1的倍数 $f[i][j][S<<1]=max(f[i][j][S<<1],f[i][m-1][S]+f[m][ ...

  4. BZOJ 4488/4052 gcd

    思路: 一开始 我是想 对于固定的左端点 从左到右 最多有 log种取值  且单调递减  那不妨倍增预处理+二分GCD在哪变了.. 复杂度O(nlog^2n) gcd最多log种取值.. 好了我们可以 ...

  5. [Luogu 2331] [SCOI2005]最大子矩阵

    [Luogu 2331] [SCOI2005]最大子矩阵 题目描述 这里有一个n*m的矩阵,请你选出其中k个子矩阵,使得这个k个子矩阵分值之和最大.注意:选出的k个子矩阵不能相互重叠. 输入输出格式 ...

  6. Gradle 自定义Task 打Jar包

    可以作为Jar包内容的有两个地方: : build/intermediates/bundles/release/ 下的classes.jar : build/intermediates/classes ...

  7. mysql自动添加时间的方法

    时间添加方法,可以在编辑数据时方便时间选择输入: 将时间列DataType设为timestamp,设定其默认值为CURRENT_TIMESTAMP. 这样每次插入一条新纪录,数据库会自动在时间段存储当 ...

  8. Centos6.6 安装Subversion服务

    一.介绍 Subversion 简称就是svn服务器,用来托管代码的,类似的还有git 1)Centos6.6 2)Subversion 二.安装 yum -y install subversion ...

  9. grep命令总结

    grep (缩写来自Globally search a Regular Expression and Print)是一种强大的文本搜索工具,它能使用特定模式匹配(包括正则表达式)搜索文本,并默认输出匹 ...

  10. jupyter notebook主目录修改

    转自http://blog.csdn.net/c437yuyang/article/details/54836303 1.打开 cmd 输入命令 jupyter notebook --generate ...