最近公共祖先(Lowest Common Ancestor)
对于有根树T的两个结点u、v,最近公共祖先LCA(u,v)表示一个结点x,满足x是u、v的祖先且x的深度尽可能大(离u和v最近的那个)。
主要是用于处理树上任意点对间的距离。(多次问询很优秀叭)
在线倍增求LCA算法大体思路:
(具体代码实现理解原理于注释中给出)
dep[]每个结点距离根节点1的深度,dis[]每个结点距离根节点1的距离,p[i][j]从节点 i 向上跳 2^j 步所到达的祖先。
1.dfs求出每个结点的深度dep[i](1为根结点),到根节点1的距离dis[i],以及父亲结点即p[i][0];
void dfs(int u,int fa,int d)
{
dep[u]=d; //求深度
p[u][0]=fa;
int sz=e[u].size();
for(int i=0;i
2.预处理出每个结点向上跳不同 2^j 步所到达的祖先结点,p[u][i]=p[p[u][i-1]][i-1],因为 2^i = 2^(i-1) + 2^(i-1) ;
void init()
{
for(int i=1;(1<
也可以在dfs时同时求出。
void dfs(int u,int fa,int d)
{
dep[u]=d; //求深度
if(u==1){ //以1为祖先
for(int i=0;i
3.对于每次问询 u,v 之间最短距离:ans = dep[u] + dep[v] - 2*dep [ LCA(u,v) ] 。
求LCA,先使u,v处于同一深度,然后特判返回,否则继续一起往上跳,因为都是倒着遍历的判定都是判定的祖先是否相同,不同时才向上跳,所以不会有跳过了的情况。
int LCA(int u,int v)
{
if(dep[u]=0;--j){ //向上跳的倍增过程,使u与v到达同一层
if((1<=0;--i){
if(p[u][i]!=p[v][i]){ //祖先仍不相同,继续往上跳
u=p[u][i];
v=p[v][i];
}
}
return p[u][0]; //注意此时u与v并不相同,而是他们得祖先相同
}
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 23476 Accepted Submission(s): 9318
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
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
直接上代码:
#include
#include
#include
using namespace std;
const int maxn=40020;
const int maxm=220;
const int maxj=20;
int n,m;
int dep[maxn]; //求深度
int dis[maxn]; //求距离
int p[maxn][maxj]; //p[i][j]表示结点i向上跳2^j步的结点
struct node{
int v,w;
node(int a,int b):v(a),w(b){}
};
vector e[maxn];
//添加边,函数区分过度了
void add_edge(int u,int v,int w)
{
e[u].push_back(node(v,w));
}
void dfs(int u,int fa,int d)
{
dep[u]=d; //求深度
/*if(u==1){ //以1为祖先
for(int i=0;i=0;--j){ //向上跳的倍增过程,使u与v到达同一层
if((1<=0;--i){
if(p[u][i]!=p[v][i]){ //祖先仍不相同,继续往上跳
u=p[u][i];
v=p[v][i];
}
}
return p[u][0]; //注意此时u与v并不相同,而是他们得祖先相同
}
int main()
{
int t;
int i,j,k;
int u,v;
scanf("%d",&t);
while(t--){
scanf("%d%d",&n,&m);
for(int x=1;x<=n;++x) //初始化
e[x].clear();
for(int x=0;x