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;
}
时间: 2024-07-30 10:18:23

LCA上的RMQ模板算法的相关文章

POJ 1330 Nearest Common Ancestors LCA(在线RMQ,离线Tarjan)

链接:http://poj.org/problem?id=1330 题意:只看题目就知道题目是什么意思了,最近公共祖先,求在一棵树上两个节点的最近公共祖先. 思路:求最近公共祖先有两种算法,在线和离线,在线方法是用RMQ求LCA,一句话总结就是在从DFS时,从第一个点到第二个点的最短路径中深度最浅的点就是公共祖先,用RMQ处理,一般问题的最优解决方式的复杂度是O(NlogN)的预处理+N*O(1)的查询.离线方法是Tarjan算法,将所有询问的两个点都记录下来,在DFS过程中不断将每个点自身作为

静态RMQ模板题 contest 静态RMQ T2

Description Wind设计了很多机器人.但是它们都认为自己是最强的,于是,一场比赛开始了.机器人们都想知道谁是最敏捷的,于是它们进行了如下一个比赛.首先,他们面前会有一排共n个数,它们比赛看谁能最先把每连续k个数中最大和最小值写下来,当然,这些机器人运算速度都很快,它们比赛的是谁写得快.但是Wind也想知道答案,你能帮助他吗? Input 每组测试数据,第1行为n,k,第2行共n个数,为数字序列,所有数字均在longint范围内. Output 共n-k+1行 第i行为第i-i+k-1

RMQ模板

附上一个详细的大佬的讲解  http://blog.csdn.net/niushuai666/article/details/6624672 RMQ模板(NYOJ 119) #include <cstdio> #include <cstring> #include <iostream> #include <map> #include <vector> #include <algorithm> #include <cmath>

POJ 3264:Balanced Lineup(RMQ模板题)

http://poj.org/problem?id=3264 题意:给出n个数,还有q个询问,询问[l,r]区间里面最大值和最小值的差值. 思路:RMQ模板题,开两个数组维护最大值和最小值就行. 1 #include <cstdio> 2 #include <cstring> 3 #include <cmath> 4 #include <algorithm> 5 using namespace std; 6 #define N 50010 7 #define

POJ 3264:Balanced Lineup Rmq模板

Balanced Lineup 题目链接: http://poj.org/problem?id=3264 题意: 求区间最大值和最小值的差 题解: Rmq模板题 代码 #include<stdio.h> #include<math.h> const int N=5e4+1; int dpmax[N][17]; int dpmin[N][17]; int mmax(int x,int y) { return x>y?x:y; } int mmin(int x,int y) {

发现一段精简的模板算法(非原创)

引用自: http://ejohn.org/blog/javascript-micro-templating/ // Simple JavaScript Templating // John Resig - http://ejohn.org/ - MIT Licensed (function(){ var cache = {}; this.tmpl = function tmpl(str, data){ // Figure out if we're getting a template, or

在一个整型数组中有一个元素的出现次数超过了数组长度的一半,试设计一个 在时间上尽可能高效的算法,找出这个元素。

题目:在一个整型数组中有一个元素的出现次数超过了数组长度的一半,试设计一个 在时间上尽可能高效的算法,找出这个元素.要求:(1)给出算法的基本设计思想.(2)根据设计思想,采用C或C++或Java语言描述算法,关键之处给出注释.(3)说明你所设计算法的时间复杂度和空间复杂度. (1)基本的设计思想: 一个数字出现的次数超过了长度的一半, 那么我们可以这样认为这个数字出现的个数一定大于其他全部数字出现的个数之和.算法的步骤如下: ①设数组为data[],数组长度为n,i=1.置currentAxi

RMQ 模板一份

 1 void RMQ_Init(const vector<int>& A) 2 { 3     int n=A.size(); 4     for (int i=0;i<n;i++) d[i][0]=A[i]; 5     for (int j=1;(1<<j)<=n;j++) 6         for (int i=0;i+(1<<j)-1<n;i++) 7             d[i][j]=min(d[i][j-1],d[i+(1

用ruby写的wikipedia上的维特比算法

维特比算法可以解决隐马尔科夫模型的最可能状态序列问题. wikipedia上关于维特比算法,提供了一个python的例子,原文地址如下 http://zh.wikipedia.org/wiki/%E7%BB%B4%E7%89%B9%E6%AF%94%E7%AE%97%E6%B3%95 鉴于最近正在学习ruby,就把这个算法从python迁移到ruby,这两个语言的语法很接近,所以,前移过去没有什么难度,希望使用代码之前先了解一下维特比算法的基础理论. # #encoding:utf-8 puts