zoukankan      html  css  js  c++  java
  • LCA转换成RMQ

    LCA(Lowest Common Ancestor 最近公共祖先)定义如下:在一棵树中两个节点的LCA为这两个节点所有的公共祖先中深度最大的节点。 

    比如这棵树

    结点5和6的LCA是2,12和7的LCA是1,8和14的LCA是4。

    这里讲一下将LCA转化成RMQ问题,进而用st表求解。

    首先我们跑一遍dfs,并记录经过的每一个结点(包括回溯的时候),存到一个数组中,这样我就将树的问题转化成线性问题。

    等下上图的树好像有些大

    这就好多了。

    然后就是dfs序列和深度序列

    dfs序   1  2  5  2  6  2  1  3  1  4  1

    dep序  1  2  3  2  3  2  1  2  1  2  1

    根据dfs的性质,从node[u]遍历到node[v]的过程中,经过LCA(u, v)有且仅有一次,而且它的深度一定是区间[u, v]中是最小的,那么这就是一个显而易见的RMQ(静态区间最小值)问题了。

    我们就以洛谷的板子为例,传送门:https://www.luogu.org/problemnew/show/P3379

    这里的vis数组是用来处理无向图存两次边的

    先写一个dfs序

     1 vector<int>v[maxn];
     2 //node存的dfs序,dep深度序列,firs[s]指s第一次出现在dfs序中的位置 
     3 int dep[2 * maxn], node[2 * maxn], firs[maxn], vis[maxn]; 
     4 int p = 1;
     5 void dfs(int s, int d)    //s用来构造dfs序,d用来构造深度序列 
     6 {
     7     vis[s] = 1;
     8     dep[p] = d; node[p] = s; firs[s] = p++;
     9     for(int i = 0; i < v[s].size(); ++i)
    10     {
    11         if(!vis[v[s][i]])
    12         {
    13             dfs(v[s][i], d + 1);    //孩子的深度肯定是父亲的深度+1 
    14             dep[p] = d; node[p++] = s;
    15         }
    16     }
    17 }

    然后考一个RMQ板子就行了

     1 #include<cstdio>
     2 #include<iostream>
     3 #include<cmath>
     4 #include<algorithm>
     5 #include<cstring>
     6 #include<vector>
     7 using namespace std;
     8 const int maxn = 5e5 + 5;
     9 int n, m, s;
    10 vector<int>v[maxn];
    11 //node存的dfs序,dep深度序列,firs[s]指s第一次出现在dfs序中的位置 
    12 int dep[2 * maxn], node[2 * maxn], firs[maxn], vis[maxn]; 
    13 int p = 1;
    14 void dfs(int s, int d)    //s用来构造dfs序,d用来构造深度序列 
    15 {
    16     vis[s] = 1;
    17     dep[p] = d; node[p] = s; firs[s] = p++;
    18     for(int i = 0; i < v[s].size(); ++i)
    19     {
    20         if(!vis[v[s][i]])
    21         {
    22             dfs(v[s][i], d + 1);    //孩子的深度肯定是父亲的深度+1 
    23             dep[p] = d; node[p++] = s;
    24         }
    25     }
    26 }
    27 //pos[L][R]表示的是区间[L, R]中最小值在dfs序中的位置 
    28 int dp[2 * maxn][25], pos[2 * maxn][25], que[2 * maxn];
    29 void init()
    30 {
    31     int n = p - 1;
    32     for(int i = 1; i <= n; ++i) {dp[i][0] = dep[i]; pos[i][0] = i;}
    33     for(int j = 1; (1 << j) <= n; ++j)
    34         for(int i = 1; i + (1 << j) - 1 <= n; ++i)
    35         {
    36             if(dp[i][j - 1] < dp[i + (1 << (j - 1))][j - 1])
    37             {
    38                 dp[i][j] = dp[i][j - 1]; pos[i][j] = pos[i][j - 1];
    39             }
    40             else
    41             {
    42                 dp[i][j] = dp[i + (1 << (j - 1))][j - 1]; pos[i][j] = pos[i + (1 << (j - 1))][j - 1];
    43             }
    44         }
    45     int k = 0;
    46     for(int i = 1; i <= n; ++i) 
    47     {
    48         if ((1 << k) <= i) k++; 
    49         que[i] = k - 1;
    50     }
    51 }
    52 void query(int L, int R)
    53 {
    54     if (R < L) swap(L, R);
    55     int k = que[R - L + 1];
    56     if(dp[L][k] <  dp[R - (1 << k) + 1][k]) printf("%d
    ", node[pos[L][k]]);
    57     else printf("%d
    ", node[pos[R - (1 << k) + 1][k]]);
    58     return;
    59 }
    60 int main()
    61 {
    62     scanf("%d%d%d", &n, &m, &s);
    63     for(int i = 1; i < n; ++i)
    64     {
    65         int a, b; scanf("%d%d", &a, &b);
    66         v[a].push_back(b); v[b].push_back(a);
    67     }
    68     dfs(s, 1);
    69     init();
    70     while(m--)
    71     {
    72         int a, b; scanf("%d%d", &a, &b);
    73         query(firs[a], firs[b]);
    74     }
    75     return 0;
    76 }
  • 相关阅读:
    mongdb 备份还原导入导出
    mongodb副本集(选举,节点设置,读写分离设置)
    mongodb副本集的内部机制(借鉴lanceyan.com)
    sqlserver 登录记录(登录触发器)
    wmic命令用法小例
    mysql查询相关的命令解析
    学习笔记:APP 瘦身 & 增加bitcode支持编译第三方框架
    关于Git的一些学习笔记
    [转]Xcode中LLDB的使用
    Swift学习笔记(2):willSet与didSet
  • 原文地址:https://www.cnblogs.com/mrclr/p/8457774.html
Copyright © 2011-2022 走看看