HDU P2586 How far away ?

1 篇文章 0 订阅

HDU P2586 How far away ?


题目

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

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

ECJTU 2009 Spring Contest


题解

倍增LCA(模板题入手……)

tip:
① 先建边(建完边后得是棵树),用DFS遍历一趟,得出各个节点到根节点的路径长,以及其深度

② 关键操作:递推,fa[i][j]=fa[i][fa[i]][i-1]][j-1] (就是i这个点向上找 2j 次父亲节点就等于i这个点向上找 xj1 次的父亲节点后再找 2j1 次父亲节点)

③ 查询,首先把深度大的那个点不断的找父亲节点直到深度与另一个点深度一样(这里i得从log(高度差)枚举到0,如果提高以后所在的位置仍大于等于另一个点的深度,那么就提高至fa[x][i],这样可以避免多次的枚举),然后把两个点一起按上面的操作继续做(只不过这里提高后两个点的 不能一样),最后,显然我们所得到任意一个点的父亲节点就是这两个点的原节点的最近公共祖先

(讲完了吧……第一次写LCA,讲的有点小瑕疵的话,欢迎大家指出)


代码

#include<cstdio>
#include<cstring>
#include<cmath>
using namespace std;

int test,n,m,tot,ans,delta;
int fa[40005][20],d[40005],lnk[40005],dep[40005];
bool vis[40005];
struct edge
{
    int nxt,y,v;
} e[80005];

int readln()
{
    int x=0;
    char ch=getchar();
    while (ch<'0'||ch>'9') ch=getchar();
    while ('0'<=ch&&ch<='9') x=x*10+ch-48,ch=getchar();
    return x;
}

void add(int x,int y,int v)
{
    tot++;e[tot].nxt=lnk[x];lnk[x]=tot;e[tot].y=y;e[tot].v=v;
}

void dfs(int x)
{
    vis[x]=true;
    for (int i=lnk[x];i;i=e[i].nxt)
    {
        int y=e[i].y;
        if (!vis[y]) {
            dep[y]=dep[x]+1;
            d[y]=d[x]+e[i].v;
            fa[y][0]=x;
            dfs(y);
        }
    }
}

int main()
{
    test=readln();
    while (test--)
    {
        memset(lnk,0,sizeof(lnk));
        memset(d,0,sizeof(d));
        memset(vis,false,sizeof(vis));
        memset(dep,0,sizeof(dep));
        tot=0;
        n=readln();m=readln();
        for (int i=1;i<n;i++)
        {
            int x=readln(),y=readln(),z=readln();
            add(x,y,z);add(y,x,z);
        }
        dfs(1);
        for (int j=1;j<=log2(n);j++) for(int i=1;i<=n;i++) fa[i][j]=fa[fa[i][j-1]][j-1];
        while (m--)
        {
            int x=readln(),y=readln(),t;
            ans=d[x]+d[y];
            if (dep[x]<dep[y]) t=x,x=y,y=t;
            delta=dep[x]-dep[y];
            for (int i=log2(delta);i>=0;i--)
            {
                if (dep[fa[x][i]]>=dep[y]) x=fa[x][i];
                if (dep[x]==dep[y]) break;
            }
            if (x!=y){
                for (int i=log2(dep[x]);i>=0;i--) if (fa[x][i]!=fa[y][i]) x=fa[x][i],y=fa[y][i];
                x=fa[x][0];
            }
            ans-=d[x]*2;
            printf("%d\n",ans);
        }
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值