欧拉路径+ST表(模板)

http://poj.org/problem?id=1330

题意:给出一颗树,给出父子关系,求两点的lca。

解法:dfs序+ST表:

原理:

欧拉序前序遍历得到的序列,叫dfs序,但数字可以重复出现,一进一出,叫欧拉序),会发现根结点总在中间,而根结点是该段序列深度最小的点

因此两个点的LCA,就是在该序列上两个点第一次出现的区间内深度最小的那个点

即在欧拉序中进行ST表在区间中找到最小深度的欧拉序。

https://www.cnblogs.com/stxy-ferryman/p/7741970.html

 

 该欧拉序为:ABDBEGEBACFHFCA.

如果要求D、G的lca通过ST表查询D、G间深度最小的欧拉序为B,即为D、G的lca。

从D到G的欧拉路径一定会经过D、G的最近公共祖先,深度最小的结点即为最近公共祖先,转为欧拉序,就是求解区间最小值问题,ST表解决。

见代码:

#include <bits/stdc++.h>
using namespace std;
const int N = 40010, M = 80010;
int pos[N];//记录每个结点的起始欧拉序下标
int oula[M], ti , dep[M];//欧拉序以及欧拉序中的下标对应结点的深度
int dp[M][29];//记录欧拉序区间的深度最小的结点
int e[M] , ne[M] , h[N] , idx ;
int n , q ;
void add(int a , int b){
    e[idx] = b , ne[idx] = h[a] , h[a] = idx++;
}

void dfs(int u, int pre , int d)
{
    oula[++ti] = u;
    dep[ti] = d ;
    pos[u] = ti;
    for (int i = h[u]; ~i; i = ne[i])
    {
        int j = e[i];
        if (j == pre)
            continue;
        dfs(j, u , d+1);
        oula[++ti] = u;
        dep[ti] = d ;
    }
}

int Min(int x , int y){//返回结点深度小的下标
    return dep[x] < dep[y] ? x : y;
}

void ST()
{
    for (int i = 1; i <= ti; i++)
    {
        dp[i][0] = i ;
    }
    for(int j = 1 ; (1 << j) <= ti ; j++){//枚举区间
        for(int i = 1 ; i + (1 << j) - 1 <= ti ; i++){
            dp[i][j] = Min(dp[i][j-1] , dp[i+(1<<j-1)][j-1]) ;
        }
    }
}
int query(int a , int b){//获取a到b路径上的最小深度的欧拉序下标
    a = pos[a] ;//映射到欧拉序中去
    b = pos[b] ;
    if(a > b) swap(a , b);
    int l = log2(b - a + 1);
    return Min(dp[a][l] , dp[b-(1<<l)+1][l]);
}

int main(){
    #ifdef ONLINE_JUDGE
    #else
        freopen("D:\\c++\\in.txt", "r", stdin);
        //freopen("D:\\c++\\out.txt", "w", stdout);
    #endif
    memset(h , -1 , sizeof(h));
    int rt ;
    cin >> n ;
    for(int i = 1 ; i <= n ; i++){
        int a , b ;
        cin >> a >> b ;
        if(b == -1){
            rt = a ;
            continue;
        }
        add(a , b);
        add(b , a);
    }
    dfs(rt , 0 , 1);
    ST();
    cin >> q ;
    while(q--){
        int a , b ;
        cin >> a >> b ;
        cout << oula[query(a , b)] << endl;//返回的是下标转为结点
    }

}

 

posted @ 2020-02-20 16:48  无名菜鸟1  阅读(289)  评论(0编辑  收藏  举报