HDU-2586 How far away ?
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 23475 Accepted Submission(s): 9317
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
LCA,通过公共祖先来简化求树中任意两点距离的算法。
学长提供的模板及解释:
void dfs(int u,int fa,int d,int dd){
dep[u] = d;//求深度
dis[u] = dd;//求距离
if(u==1){
for(int i = 0;i<20;i++)par[u][i] = 1;/*以u为1祖先 那么不管1怎么跳都只能跳到自己*/
}
else{
par[u][0] = fa;/*跳2^0 = 1 步就是跳到他爸爸*/
for(int i = 1;i<20;i++){
par[u][i] = par[par[u][i-1]][i-1];
/*
怎么理解呢?
我们先分析一下 2 = 1+ 1,4 = 2+2, 8 = 4+4;
其实就是2^n = 2^(n-1) + 2^(n-1);
那么u跳2^n步所到的点就等于 u 跳到2^(n-1)步跳到的祖先(假设为v)再跳2^(n-1)步所到的点
*/
}
}
for(auto e:G[u]){
int v = e.fi,c = e.se;
if(v==fa)continue;
dfs(v,u,d+1,dd+c);
}
}
int Jump(int u,int d){
for(int j = 19;j>=0;j--){
if((1<<j)&d){
u = par[u][j];
/*
10010
10000
00010
*/
}
}
return u;
}
int lca(int u,int v){
if(dep[u]<dep[v])swap(u,v);
u = Jump(u,dep[u]-dep[v]);
/*
先找到深度更大的点往上跳,让它与另外一个
深度更小的点同深度。
*/
if(u==v)return u;
/*
特判一下
如果两点相同表明在同一颗子树直接返回就好了。
*/
for(int i = 19;i>=0;i--){
if(par[u][i]!=par[v][i]){
/*跳2^i 步仍没有找到公共祖先,表明u和v都需要往上跳2^i步*/
u = par[u][i];
v = par[v][i];
}
}
return par[u][0];
}
自己写的应用代码
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <vector>
using namespace std;
const int MAXL=40004;
int n,m,t;
int dep[MAXL],dis[MAXL];
int par[MAXL][18];
vector<pair<int ,int> > tree[MAXL];
void pre_dfs(int k,int fa,int d,int dd)
{
dep[k]=d;
dis[k]=dd;
int len=tree[k].size();
if(k==1)
for(int i=0;i<18;i++) par[k][i]=1;
else
{
par[k][0]=fa;
for(int i=1;i<18;i++)
par[k][i]=par[par[k][i-1]][i-1];//biru 2^k=2^(k-1)*2 jwzi
}
for(int i=0;i<len;i++)
{
int j=tree[k][i].first,val=tree[k][i].second;
pre_dfs(j,k,d+1,dd+val);
}
}
int skip(int a,int level)
{
for(int i=17;i>=0;i--)
{
if(1<<i&level)
a=par[a][i];
}
return a;
}
int lca(int a,int b)
{
if(dep[a]<dep[b]) swap(a,b);
a=skip(a,dep[a]-dep[b]);
if(a==b) return a;
for(int i=17;i>=0;i--)
{
if(par[a][i]!=par[b][i])
{
a=par[a][i];
b=par[b][i];
}
}
return par[a][0];
}
int main(void)
{
scanf("%d",&t);
while(t--)
{
scanf("%d%d",&n,&m);
int x,y,val;
for(int i=0;i<n-1;i++)
{
scanf("%d%d%d",&x,&y,&val);
if(x>y) swap(x,y);
tree[x].push_back(make_pair(y,val));
}
pre_dfs(1,0,1,0);
for(int i=0;i<m;i++)
{
scanf("%d%d",&x,&y);
int fa,res;
fa=lca(x,y);
res=dis[x]+dis[y]-dis[fa]*2;
printf("%d\n",res);
}
}
return 0;
}