LCA 0.3

来源:互联网 发布:c语言图形函数库 编辑:程序博客网 时间:2024/05/17 04:44

LCA

0.3


目录

  • LCA
    • 基本概念
    • Tarjan
      • 大致思路
      • 实现过程
    • 倍增
      • 基本思路
      • 实现过程


基本概念

LCA是求最近公共祖先问题,有在线st/RMQ和离线Tarjan/并查集两种算法,以及倍增的特殊做法 (倍增是一种思想). 虽然倍增的做法更常见,但是两种都要遍历一次树,直接处理问题的Tarjan在时间上更好.选择更好的算法,而不是更多的常数优化应该是编程的好习惯 (仅供参考) .
Tarjan算法只需要遍历一次树 (一边遍历一边查询,所以要先存储询问) ,所以其时间复杂度是O(n+m (询问个数) ).

Tarjan

大致思路

  1. 从根节点出发开始搜索
  2. 找到所有子节点并继续搜索
  3. 在搜索过程中记录每个访问的点的lca

实现过程

int find(int i){    if(i!=f[i]) f[i]=find(f[i]);    return f[i];}void lca(int u){    v[u]=true;//标记以访问过    for(int i=first[u];i;i=a[i].ne)//链式前向星在遍历时比较方便且省时间    {        int e=a[i].t;        if(v[e]) continue;        //因为不清楚父子关系,为避免环,需要加一个标记        lca(e);//拓展e和它的子节点,同时遍历        f[e]=u;//将e并入u    }    for(int i=firs[u];i;i=b[i].ne)    {        int e=b[i].t;        if(!v[e]) continue;//还没有访问到的点的父亲是它自己        if(i&1)        //直接将答案与询问边一起存储,注意输出时要把i<<1 (双向存储询问)            b[i+1].ans=b[i].ans=find(e);        else            b[i-1].ans=b[i].ans=find(e);    }}

倍增

基本思路

倍增是一种快速的搜索方式,可以在较短的时间找到目标,但是因为是在线算法,在时间开销上可能比Tarjan大.简单来说,倍增就是跳跃式前进,以计算机习惯的运动方式 (二进制).预处理为O(nlogn),查询为常数级 (最多只需要到31) ,基本看成O(1).在询问数较大时可能会快一点 (但是因为不管是哪种算法都需要极大的空间开销,所以一般不需要作此类考虑)

实现过程

void dfs(int u){    d[u]=d[f[u][0]]+1;//子节点比父节点深度多1    for(int i=0;f[u][i];i++)        f[u][i+1]=f[f[u][i]][i];        //可以结合关系并查集理解,简单的关系转换    for(int i=first[u];i;i=a[i].ne)        if(!d[a[i].t])//如果没有处理过        {            f[a[i].t][0]=u;//f[e]=u            dfs(a[i].t);//继续拓展        }}int lca(int u,int v){    if(d[u]>d[v])//位运算交换        u^=v^=u^=v;    for(int i=bb;i>=0;i--)        if(d[f[v][i]]>=d[u])            v=f[v][i];    if(u==v)        return v;    for(int i=bb;i>=0;i--)        if(f[v][i]!=f[u][i])        {            v=f[v][i];            u=f[u][i];        }    return f[u][0];//直接输出即可}int dete(unsigned x)//不用unsigned就RE,因为数字非常大 (十进制下)//bb=dete(n),用来判断位数,小优化.在卡常时效果会更好{    int n=1;    if(x==0) return -1;    if ((x>>16) == 0) {n = n+16; x = x<<16;}    if ((x>>24) == 0) {n = n+8; x = x<<8;}    if ((x>>28) == 0) {n = n+4; x = x<<4;}    if ((x>>30) == 0) {n = n+2; x = x<<2;}    n = n-(x>>31);    return 31-n;}
原创粉丝点击