HDU-2586 LCA入门模板

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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值