zoukankan      html  css  js  c++  java
  • LCA-RMQ+欧拉序

    还是那一道洛谷的板子题来说吧

    传送门

    其实好几天之前就写了

    结果dr实在是太弱了

    没有那么多的精力

    于是就一直咕咕咕了

    今天终于补上来了

    LCA概念传送门

    RMQ传送门

    这个算法是基于RMQ和欧拉序的算法

    什么是Rmq?
     
    RMQ (Range Minimum/Maximum Query)问题是指:
    对于长度为n的数列A,
    回答若干询问RMQ(A,i,j)(i,j<=n),
    返回数列A中下标在i,j里的最小(大)值,
    也就是说,RMQ问题是指求区间最值的问题。
    解决Rmq问题常用ST算法。
     
    ST算法时间复杂度:
     预处理O(nlogn)
     单次查询O(1)
     
    欧拉序?
     欧拉序是一种树的遍历顺序,其他还有dfs序,这些序具有一定的性质。
     欧拉序是树dfs过程中经过 结点的顺序。
    若已1->2->4->5->6->7->3的顺序dfs树

    即每经过一次结点就记录一次,

    n个结点的树有2n-1个记录

    基于Rmq和欧拉序的Lca算法:

     预处理出树的欧拉序,预处理id,vs,depth数组

    id[u]表示结点u第一次被访问时的下标,

    vs[i]表示欧拉序中第i个结点的编号,

    depth[i]表示欧拉序中第i个结点的深度。

    假设dfs顺序1->2->4->5->3

     

    要求Lca(u,v),例如Lca(4,5),
    根据dfs的性质,在第一次访问完结点4及其子树,回溯,
    再第一次访问到5的过程中,途中访问过的深度最小的结点必是Lca(4,5)
     
    Rmq问题。Lca(u,v)=vs[id[u]<=i<=id[v]中depth[i]最小的i]
    (假设id[u]<id[v])
     
     
    #include<cstdio>
    #include<algorithm>
    using namespace std;
    
    const int maxn = 500005;
    const int maxm = 1000005;
    int head[maxn],nxt[maxm],to[maxm],cnt;
    int id[maxm],vis[maxm],depth[maxm],tot;
    int f[maxm][20],lg[maxm];
    
    inline int read()
    {
        int sum = 0,p = 1;
        char ch = getchar();
        while(ch < '0' || ch > '9')
        {
            if(ch == '-')
                p = -1;
            ch = getchar();
        }
        while(ch >= '0' && ch <= '9')
        {
            (sum *= 10)+= ch - '0';
            ch = getchar();
        }
        return sum * p;
    }
    
    void add(int x,int y)//链式前向星--加边 
    {
        nxt[++cnt] = head[x];
        to[cnt] = y;
        head[x] = cnt;
        return;
    }
    
    void dfs(int u,int fa,int dep)
    {
        id[u] = ++tot;
        vis[tot] = u;
        depth[tot] = dep;
        for(int i = head[u];i;i = nxt[i])
        {
            int v = to[i];
            if(v == fa)
                continue;
            dfs(v,u,dep+1);
            vis[++tot] = u;
            depth[tot] = dep;
        }
        return;
    }
    
    void RMQ()
    {
        for(int i = 1;i <= tot;i++)
            lg[i] = lg[i - 1] + (1 << lg[i - 1] == i);
        for(int i = 1;i <= tot;i++)
            f[i][0] = i;
        for(int j = 1;(1 << j) <= tot;j++)
            for(int i = 1;i + (1 << j) - 1 <= tot;i++)
            {
                int a = f[i][j-1];
                int b = f[i + (1 << (j - 1))][j - 1];
                if(depth[a] <= depth[b])
                    f[i][j] = a;
                else
                    f[i][j] = b;
            }
        return;
    }
    
    int    st(int x,int y)
    {
        int r = id[x];
        int l = id[y];
        if(r < l)
            swap(r,l);
        int k = lg[r - l + 1] - 1;
        int a = f[l][k];
        int b = f[r - (1 << k) + 1][k];
        if(depth[a] <= depth[b])
            return vis[a];
        else
            return vis[b];
    }
    
    int main()
    {
        int n = read(),m = read(),s = read();
        int x,y;
        for(int i = 1;i < n;i++)
        {
            x = read(),y = read();
            add(x,y);
            add(y,x);
        }
        dfs(s,0,1); 
        RMQ();
        for(int i = 1;i <= m;i++)
        {
            x = read(),y = read();
            printf("%d
    ",st(x,y));
        }
        return 0;
    } 
  • 相关阅读:
    如何设置mysql数据库为只读
    华为S5300系列、S5700系列交换机无法修改密码问题分析
    一个form表单有两个按钮,分别提交到不同的页面
    在cmd/bat脚本中获取当前脚本文件所在目录
    以一个学生宿舍区为例,解析华为交换机AAA的配置
    mysql创建远程用户并授权
    锐捷交换机中的password与secret的区别
    机器学习基础及案例
    python所有基础
    win10找不到Hyper-V的解决方法
  • 原文地址:https://www.cnblogs.com/darlingroot/p/10612077.html
Copyright © 2011-2022 走看看