现在的位置: 首页 > 综合 > 正文

RMQ-ST 解决在线LCA

2018年04月12日 ⁄ 综合 ⁄ 共 4995字 ⁄ 字号 评论关闭

RMQ 按照统计的量来归类:

1  统计[l,r]区间 最大、最小的 节点下标(如LCA,找深度最浅的下标)

2 统计[l,r]区间总的xxx

 


描述

上上回说到,小Hi和小Ho使用了Tarjan算法来优化了他们的“最近公共祖先”网站,但是很快这样一个离线算法就出现了问题:如果只有一个人提出了询问,那么小Hi和小Ho很难决定到底是针对这个询问就直接进行计算还是等待一定数量的询问一起计算。毕竟无论是一个询问还是很多个询问,使用离线算法都是只需要做一次深度优先搜索就可以了的。

那么问题就来了,如果每次计算都只针对一个询问进行的话,那么这样的算法事实上还不如使用最开始的朴素算法呢!但是如果每次要等上很多人一起的话,因为说不准什么时候才能够凑够人——所以事实上有可能要等上很久很久才能够进行一次计算,实际上也是很慢的!

“那到底要怎么办呢?在等到10分钟,或者凑够一定数量的人两个条件满足一个时就进行运算?”小Ho想出了一个折衷的办法。

“哪有这么麻烦!别忘了和离线算法相对应的可是有一个叫做在线算法的东西呢!”小Hi笑道。

小Ho面临的问题还是和之前一样:假设现在小Ho现在知道了N对父子关系——父亲和儿子的名字,并且这N对父子关系中涉及的所有人都拥有一个共同的祖先(这个祖先出现在这N对父子关系中),他需要对于小Hi的若干次提问——每次提问为两个人的名字(这两个人的名字在之前的父子关系中出现过),告诉小Hi这两个人的所有共同祖先中辈分最低的一个是谁?

提示:最近公共祖先无非就是两点连通路径上高度最小的点嘛!

输入

每个测试点(输入文件)有且仅有一组测试数据。

每组测试数据的第1行为一个整数N,意义如前文所述。

每组测试数据的第2~N+1行,每行分别描述一对父子关系,其中第i+1行为两个由大小写字母组成的字符串Father_i, Son_i,分别表示父亲的名字和儿子的名字。

每组测试数据的第N+2行为一个整数M,表示小Hi总共询问的次数。

每组测试数据的第N+3~N+M+2行,每行分别描述一个询问,其中第N+i+2行为两个由大小写字母组成的字符串Name1_i, Name2_i,分别表示小Hi询问中的两个名字。

对于100%的数据,满足N<=10^5,M<=10^5, 且数据中所有涉及的人物中不存在两个名字相同的人(即姓名唯一的确定了一个人),所有询问中出现过的名字均在之前所描述的N对父子关系中出现过,且每个输入文件中第一个出现的名字所确定的人是其他所有人的公共祖先

输出

对于每组测试数据,对于每个小Hi的询问,按照在输入中出现的顺序,各输出一行,表示查询的结果:他们的所有共同祖先中辈分最低的一个人的名字。

样例输入
4
Adam Sam
Sam Joey
Sam Micheal
Adam Kevin
3
Sam Sam
Adam Sam
Micheal Kevin
样例输出
Sam
Adam
Adam

 

 


 

#include <iostream>
#include <map>
#include <string.h>
#include <vector>
using namespace std;
int L=0, R=999999;//R should be last idx of sorted-visitQue[], not known yet so as maxn
int t[1000000];
int s[1000000];
int m[1000000]; // idx of min depth
int d[1000000];

int get(int l, int r, int idx, int a, int b){
    //cout<<"get:"<<l<<":"<<r<<":"<<a<<":"<<b<<endl;
    if(a<=l && b>=r) return m[idx];
    int mid=(l+r)/2;//@error: not (a+b)/2
    int s=-1, t=-1;
    if(a<=mid) s=get(l, mid, idx*2+1, a, min(mid, b));//@error: not a,mid
    if(b>mid) t=get(mid+1, r, idx*2+2, max(a, mid+1), b);//@error: not mid+1,b
    if(s!=-1&&t!=-1){
        return d[s]<d[t]?s:t;
    }
    return s==-1?t:s;
}
void set(int l, int r, int idx, int a){
    if(l==r) {m[idx]=l; return;}
    int s=m[idx];
    if(s==-1 || d[s]>d[a]) m[idx]=a;
    int mid=(l+r)/2;
    if(a<=mid) set(l, mid, idx*2+1, a);
    else set(mid+1, r, idx*2+2, a);
}

void dfs(vector<vector<int> >&tree, int& num, int dep, int p, int v){
    for(int i=0; i<tree[v].size(); i++){
        int u=tree[v][i];
        if(u==p) continue;
        dfs(tree, ++num, dep+1, v, u);
        t[++num]=v, s[v]=num, d[num]=dep;
        set(L, R, 0, num);
    }
    t[++num]=v, s[v]=num, d[num]=dep;
    set(L, R, 0, num);
}

int main(){
    int n; cin>>n;
    int num=0;
    map<string, int> person;
    vector<string> name(n+1);
    vector<vector<int> > tree(n+1, vector<int>());
    for(int i=0; i<n; i++){
        string s, t; cin>>s>>t;
        if(!person.count(s)) { name[num]=s, person[s]=num++; }
        if(!person.count(t)) { name[num]=t, person[t]=num++; }
        int l=person[s], r=person[t];
        tree[l].push_back(r);
    }
    num=0;
    memset(m, -1, sizeof(m));
    dfs(tree, num, 0, -1, 0);

    int m; cin>>m;
    for(int i=0; i<m; i++){
        string a, b;
        cin>>a>>b;
        int l=person[a], r=person[b];
        int d=get(L, R, 0, min(s[l],s[r]), max(s[l],s[r]));
        //cout<<"get the node in sorted-visit-queue idxed:"<<d<<endl;
        cout<<name[t[d]]<<endl;
    }
    return 0;
}

版本二

#include <iostream>
#include <assert.h>
#include <vector>
#include <map>
using namespace std;

vector<int> st;
vector<int> hs, nodes, ss;
vector<string> names;
vector<vector<int> > graph;

// get visit-seq: nodes[] using dfs 
void dfs(int h, int v){
    hs[v] = h, ss[v] = nodes.size();
    
    nodes.push_back(v);
    for(int i = 0; i<graph[v].size(); i++){
        int u = graph[v][i];
        dfs(h+1, u);
        nodes.push_back(v);
    }
}

// init ST using nodes[] and hs[Node]
void init(int l, int r, int idx){
    if(l==r) { st[idx] = nodes[l]; return; }
    int mid = (l+r)/2;
    init(l, mid, idx*2+1);
    init(mid+1, r, idx*2+2);
    st[idx] = (hs[st[idx*2+1]]>hs[st[idx*2+2]]?st[idx*2+2]:st[idx*2+1]);
}

int get(int l, int r, int idx, int a, int b){
    if(a<=l && b>=r) return st[idx];
    int mid = (l+r)/2;
    int e = -1, e1 = -1;
    if(a<=mid)  e = get(l, mid, idx*2+1, a, b);
    if(b> mid)  e1 = get(mid+1, r, idx*2+2, a, b);
    
    if(e == -1) e = e1, e1 = -1;
    if(e1 != -1) e = (hs[e]>hs[e1]?e1:e);
    return e;
}

int main(){
    int n; cin>>n;
    map<string, int> nums; int idx = 0;
    graph.resize(n*2), hs.resize(n*2), ss.resize(n*2);
    vector<int> rd(n*2, 0);
    for(int i = 0; i< n; i++){
        string a, b; cin>>a>>b;
        if(!nums.count(a)){ nums[a] = idx++, names.push_back(a);}
        if(!nums.count(b)){ nums[b] = idx++, names.push_back(b);}
        int ai = nums[a], bi = nums[b];
        graph[ai].push_back(bi);
        rd[bi]++;
    }
    
    /* @warning: 可以处理森林吗? 不行,该lca算法基于单根树假设。
    for(int i = 0; i<names.size(); i++){
      if(rd[i]==0) dfs(0, i);
    }*/

    st.resize(nodes.size() * 4); // ST size !!!
    init(0, nodes.size()-1, 0);

    int m; cin>>m;
    for(int i = 0; i<m; i++){
        string a, b; cin>>a>>b;
        int ai = nums[a], bi = nums[b];
        ai = ss[ai], bi = ss[bi];
        int s = get(0, nodes.size()-1, 0, min(ai, bi), max(ai, bi)); //@error: <ai, bi> may not be right order
        cout<<names[s]<<endl;
    }
    return 0;
}

抱歉!评论已关闭.