LCA上的RMQ模板算法

Posted AC菜鸟机

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了LCA上的RMQ模板算法相关的知识,希望对你有一定的参考价值。

How far away ?

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 10764    Accepted Submission(s): 3923


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
 
 
这题可以用离线算法做,我这里介绍的是RMQ算法 。。
RMQ算法就是求区间最值问题的算法,这里用到最近公共祖先上面。是根据每个点的编号以及深度来确定最近公共祖先。
贴模板:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
using namespace std;
#define N 40010
#define M 205

struct Edge{
    int u,v,w,next;
}e[2*N];
int head[N];
int dp[2*N][20];  ///
int vis[N];
int first[N];
int deep[2*N];  ///记录每个点每次出现出现所在的深度,由于回溯,遂开两倍
int id[2*N];  ///结点的编号,也会出现两次
int dis[N];
int tot;

int MIN(int i,int j){
    if(i>=j) return j;
    return i;
}
void add_edge(int u,int v,int w,int & k){
    e[k].u=u;e[k].v=v;e[k].w = w;
    e[k].next=head[u];head[u]=k++;
}
void dfs(int u,int d){
    vis[u]=true;id[++tot]=u;first[u]=tot;deep[tot]=d;
    for(int k = head[u];k!=-1;k=e[k].next){
        if(!vis[e[k].v]){
            int v = e[k].v,w = e[k].w;
            dis[v]=dis[u]+w;
            dfs(v,d+1);
            id[++tot]=u,deep[tot]=d;
        }
    }
}
void init_RMQ(int n){
    for(int i=1;i<=n;i++){
        dp[i][0]=i; ///保存的是下标
    }
    for(int j=1;(1<<j)<=n;j++){
        for(int i=1;i+(1<<j)-1<n;i++){
            dp[i][j] = MIN(dp[i][j-1],dp[i+(1<<(j-1))][j-1]);
        }
    }
}
int RMQ(int L,int R){
    int k=0;
    while((1<<(k+1))<=(R-L+1)) k++;
    int a = dp[L][k],b = dp[R-(1<<k)+1][k];
    if(deep[a]<deep[b]) return a;
    return b;
}
int LCA(int L,int R){
    int x = first[L];
    int y = first[R];
    if(x>y) swap(x,y);
    return id[RMQ(x,y)];
}
int main()
{
    int tcase;
    scanf("%d",&tcase);
    while(tcase--){
        memset(head,-1,sizeof(head));
        memset(vis,0,sizeof(vis));
        int n,m;
        scanf("%d%d",&n,&m);
        tot = 0;
        for(int i=1;i<n;i++){
            int u,v,w;
            scanf("%d%d%d",&u,&v,&w);
            add_edge(u,v,w,tot);
            add_edge(v,u,w,tot);
        }
        tot = 0;
        dis[1]=0;
        dfs(1,1);
        init_RMQ(2*n-1);
        while(m--){
            int a,b;
            scanf("%d%d",&a,&b);
            int lca = LCA(a,b);
            printf("%d\n",dis[a]+dis[b]-2*dis[lca]);
        }
    }
    return 0;
}

 

以上是关于LCA上的RMQ模板算法的主要内容,如果未能解决你的问题,请参考以下文章

P3379 模板最近公共祖先(LCA)(欧拉序+rmq)

hdu 2586 + hdu 4123(RMQ算法与LCA)

LCA 算法ST表

LCA转RMQ 方法(预览)

谈谈RMQ算法

LCA学习笔记