HDU2586-How far away ?(LCA 树上两点路径 tarjan和RMQ做法)

题目链接

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


题意

给一棵无根树,n个点,n-1个边,每个边有权值表示距离,问任意两点的距离

思路

任意选择一个作为根,dis[ i ] 表示 i 结点到根的距离,用DFS走一遍数算出dis 。然后求x和y的距离,转化为求x和y的LCA问题,他们的最小公共祖先p,则x和y的距离就是 dis[ x ] + dis[ y ]  -  2 *  dis[ p ].

LCA用tarjan求,先把访问的点x,y存在数组里,DFS的时候记得查两次。

 

另一种是LCA转RMQ的做法,先用数组保存DFS序,保存结点的深度,最小公共祖先就是区间的最小值,即深度最小。


#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <map>
#define ll long long
#define inf 0x3f3f3f3f

using namespace std;
const int maxn = 40010;

struct Edge
{
    int to,next,w;
}edge[maxn*2];

int head[maxn],tot=0;
void addEdge(int from,int to,int d)
{
    edge[tot].to = to; edge[tot].w = d;
    edge[tot].next = head[from];
    head[from] = tot++;
}

int n,m,x,y,d,ans[210],vis[maxn],f[maxn],dis[maxn],query[210][2];

int Find(int x)
{
    if(x==f[x]) return x;
    else return f[x] = Find(f[x]);
}

void tarjan(int u)
{
    vis[u] = 1;
    for(int i=head[u];i!=-1;i=edge[i].next){
        int v = edge[i].to;
        if(vis[v]) continue;
        tarjan(v);
        f[v] = u;
    }
    vis[u] = 2;
    for(int i=0;i<m;i++){
        int x = query[i][0], y = query[i][1];
        if(u==x&&vis[y]==2) ans[i] = Find(y);
        if(u==y&&vis[x]==2) ans[i] = Find(x);
    }
}

void dfs(int x,int fa)
{
    for(int i=head[x];i!=-1;i=edge[i].next){
        int u = edge[i].to;
        if(u==fa) continue;
        dis[u] = dis[x] + edge[i].w;
        dfs(u,x);
    }
}

int main()
{
    int T; scanf("%d",&T);
    while(T--)
    {
        tot = 0;
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++){
            f[i] = i; head[i] = -1; vis[i] = 0; dis[i] = 0;
        }
        for(int i=1;i<n;i++){
            scanf("%d%d%d",&x,&y,&d);
            addEdge(x,y,d); addEdge(y,x,d);
        }
        for(int i=0;i<m;i++){
            scanf("%d%d",&x,&y);
            query[i][0] = x; query[i][1] = y;
        }
        dfs(1,0);
        tarjan(1);
        for(int i=0;i<m;i++) printf("%d\n",dis[query[i][0]]+dis[query[i][1]]-2*dis[ans[i]]);
    }
    return 0;
}

LCA转RMQ做法

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <map>
#define ll long long
#define inf 0x3f3f3f3f

using namespace std;
const int maxn = 40010;

int m,n,q,x,y,c,d,ST_Min[maxn*2][20];

struct Edge
{
    int to,next,w;
}edge[maxn*2];
int head[maxn],tot=0;
void addEdge(int from,int to,int d)
{
    edge[tot].to = to; edge[tot].w = d; edge[tot].next = head[from];
    head[from] = tot++;
}

int cnt,num[maxn*2],deep[maxn*2],first[maxn],dis[maxn];
void dfs(int u,int fa,int dep)
{
    num[++cnt] = u;
    deep[cnt] = dep;
    first[u] = cnt;
    for(int i=head[u];i!=-1;i=edge[i].next){
        int v = edge[i].to, w = edge[i].w;
        if(v==fa) continue;
        dis[v] = dis[u] + w;
        dfs(v,u,dep+1);
        num[++cnt] = u;
        deep[cnt] = dep;
    }
}
void ST_Init(int n)
{
    for(int i=1;i<=n;i++) ST_Min[i][0] = i;
    for(int j=1;(1<<j)<=n;j++){
        for(int i=1;i+(1<<j)-1<=n;i++){
            int a = ST_Min[i][j-1], b = ST_Min[i+(1<<(j-1))][j-1];
            if(deep[a]<=deep[b]) ST_Min[i][j] = a;
            else ST_Min[i][j] = b;
        }
    }
}
int query_min(int l,int r)
{
    int k = (int)(log(r-l+1.0)/log(2.0));
    int a = ST_Min[l][k], b = ST_Min[r-(1<<k)+1][k];
    if(deep[a]<=deep[b]) return a;
    else return b;
}
int LCA(int u,int v)
{
    int x = first[u], y = first[v];
    if(x>y) swap(x,y);
    return num[query_min(x,y)];
}

int main()
{
    int T;scanf("%d",&T);
    while(T--)
    {
        scanf("%d%d",&n,&q);
        for(int i=1;i<=n;i++){
            head[i] = -1; dis[i] = deep[i] = first[i] = 0;
        }
        tot = 0;
        for(int i=1;i<n;i++){
            scanf("%d%d%d",&x,&y,&d);
            addEdge(x,y,d); addEdge(y,x,d);
        }
        cnt = 0;
        dfs(1,-1,0);
        ST_Init(2*n-1);

        for(int i=1;i<=q;i++){
            scanf("%d%d",&x,&y);
            int lca = LCA(x,y);
           // printf("x:%d  y:%d   lca:%d   ::%d\n",dis[x],dis[y],dis[lca],lca);
            printf("%d\n",dis[x]+dis[y]-2*dis[lca]);
        }
    }
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值