• 最近公共祖先 LCA 倍增法


    【简介】

          解决LCA问题的倍增法是一种基于倍增思想的在线算法。

    【原理】

         原理和同样是使用倍增思想的RMQ-ST 算法类似,比较简单,想清楚后很容易实现。

         对于每个节点u , ancestors[u][k] 表示 u 的第2k个祖先是谁。很容易就想到递推式: ancestors[j][i] = ancestors[ancestors[j][i - 1]][i - 1];  根据二进制原理,理论上 u 的所有祖先都可以根据ancestors数组多次跳转得到,这样就间接地记录了每个节点的祖先信息。
         查询LCA(u,v)的时候:
             (一)u和v所在的树的层数如果一样,令u'=u。否则需要平衡操作(假设u更深),先找到u的一个祖先u', 使得u'的层数和v一样,此时LCA(u,v)=LCA(u',v) 。证明很简单:如果LCA(u,v)=v , 那么u'一定等于v ;如果LCA(u,v)=k ,k!=v ,那么k 的深度一定小于 v , u、u'、v 一定在k的子树中;综上所述,LCA(u,v)=LCA(u',v)一定成立。

             (二)此时u' 和 v 的祖先序列中一开始的部分一定有所重叠,重叠部分的最后一个元素(也就是深度最深,与u'、v最近的元素)就是所求的LCA(u,v)。这里ancestors数组就可以派上用场了。找到第一个不重叠的节点k,LCA(u,v)=ancestors[k][0] 。 找k的过程利用二进制贪心思想,先尽可能跳到最上层的祖先,如果两祖先相等,说明完全可以跳小点,跳的距离除2,这样一步步跳下去一定可以找到k。

    【hdu 2586】

          需要注意的是超界的处理。

      1 #pragma comment(linker, "/STACK:1024000000,1024000000")
      2 #include <stdio.h>
      3 #include <string.h>
      4 #include <vector>
      5 #include <cmath>
      6 #include <iostream>
      7 using namespace std;
      8 int n,m;
      9 struct edge
     10 {
     11     int d,v,next;
     12     edge(){}
     13     edge(int _d,int _v,int _next)
     14     {
     15         d=_d;v=_v;next=_next;
     16     }
     17 }data[80003];
     18 int map[40003];
     19 int pool;
     20 void addedge(int s,int e,int v)
     21 {
     22     int t=map[s];
     23     data[pool++]=edge(e,v,t);
     24     map[s]=pool-1;
     25 }
     26 int ANCLOG;
     27 int depth[40003];
     28 int ifv[40003];
     29 int dis[40003];
     30 int anc[40003][17];
     31 void dfs(int cur,int dep)
     32 {
     33     ifv[cur]=1;
     34     depth[cur]=dep;
     35     int p=map[cur];
     36     while (p!=-1)
     37     {
     38        if (!ifv[data[p].d])
     39        {
     40            dis[data[p].d]=dis[cur]+data[p].v;
     41            anc[data[p].d][0]=cur;
     42            dfs(data[p].d,dep+1);
     43        }
     44         p=data[p].next;
     45     }
     46 }
     47 void initLCA()
     48 {
     49     for (int k=1;k<ANCLOG;++k)
     50        for (int i=0;i<n;++i)
     51     {
     52         if (anc[i][k-1]==-1) continue;
     53         anc[i][k]=anc[anc[i][k-1]][k-1];
     54     }
     55 }
     56 int getLCA(int u,int v)
     57 {
     58     if (depth[u]<depth[v]) swap(u,v);
     59     for (int k=ANCLOG;k>=0;--k)
     60     {
     61         if (anc[u][k]==-1) continue;
     62         if (depth[anc[u][k]]>=depth[v])
     63         {
     64             u=anc[u][k];
     65             if (depth[u]==depth[v]) break;
     66         }
     67     }
     68     if (u==v) return u;
     69     for (int k=ANCLOG;k>=0;--k)
     70     {
     71          if (anc[u][k]==-1) continue;
     72          if (anc[u][k]!=anc[v][k])
     73          {
     74              u=anc[u][k];
     75              v=anc[v][k];
     76          }
     77     }
     78     return anc[u][0];
     79 }
     80 int main()
     81 {
     82     int T;
     83     scanf("%d",&T);
     84     while (T--)
     85     {
     86         pool=0;
     87         memset(anc,-1,sizeof anc);
     88         memset(map,-1,sizeof map);
     89         memset(ifv,0,sizeof ifv);
     90         scanf("%d%d",&n,&m);
     91         ANCLOG=(int)(log(n)/log(2.0));
     92         int s,e,v;
     93         for (int i=0;i<n-1;++i)
     94         {
     95             scanf("%d%d%d",&s,&e,&v);
     96             addedge(s-1,e-1,v);
     97             addedge(e-1,s-1,v);
     98         }
     99         dis[0]=0;
    100         dfs(0,0);
    101         initLCA();
    102         for (int i=0;i<m;++i)
    103         {
    104             int u,v;
    105             scanf("%d%d",&u,&v);
    106             --u;--v;
    107             int k=getLCA(u,v);
    108             k=dis[u]+dis[v]-2*dis[k];
    109             printf("%d
    ",k);
    110         }
    111     }
    112 }
    View Code
  • 相关阅读:
    GitHub常用 库
    App性能优化
    iOS App性能优化
    UIButton图片与文字位置调整
    Mac常用目录
    js数字转金额,ajax调用接口,后台返回html(完整页面),打开新窗口并写入html
    js坑 把数字型的字符串默认为数字 把前面的0给去掉了("001")
    url跳转路径参数获取
    常用正则表达式,手机号,邮箱,网址
    Js获取操作系统版本 && 获得浏览器版本
  • 原文地址:https://www.cnblogs.com/wuminye/p/3532397.html
Copyright © 2020-2023  润新知