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;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章