zoukankan      html  css  js  c++  java
  • LCA倍增算法

    LCA 算法是一个技巧性很强的算法。

    十分感谢月老提供的模板。

    这里我实现LCA是通过倍增,其实就是二进制优化。

    任何一个数都可以有2的阶数实现

    例如16可以由1 2 4 8组合得到

    5可以由1 2 4 组合得到

    便于读者理解 我放一道例题吧

    Problem F: 挑战迷宫

    Description

    小翔和小明正在挑战一个神奇的迷宫。迷宫由n个房间组成,每个房间的编号为1~n,其中1号房间是他们俩初始位置,
    所有房间一共由n-1条路连接,使得房间两两之间能够相互达到(构成一棵树),每条路的长度为Wi。
      每当小翔和小明都在房间时,他们的神奇手机就能显示两人的位置(两人分别在哪两个房间),现在想请聪明的ACMer
    快速地算出他们之间的最短距离。

    Input

      第一行输入整数n(0<n<=100000),表示迷宫有n个房间。
      接下来n-1行,每行输入3个整数u,v,w(1<=u,v<=n,u!=v,w<=10000),表示编号为u和v的房间之间有一条长为w的路。
      第n+1行输入整数m(0<m<=100000),表示有m次询问。
      接来下m行,每行输入2个整数u,v(1<=u,v<=n),表示小翔和小明当前所在房间的编号。

    Output

    对于每次询问的输出占一行,输出一个整数d表示小翔和小明之间的最短距离。

    Sample Input

    4
    1 2 1
    2 3 1
    1 4 1
    1
    3 4

    Sample Output

    3

    这是CSUST选拔赛的一题,表示当时不会LCA 菜的抠脚 (菜是原罪啊)
    注意这题时间为1S N为1e6 最短路肯定是不行的,复杂度不行。
    n个点n-1条路 保证联通 其实就是每一个点到另外一个点有唯一的路径。
    然后这就是一个非常非常裸的LCA。
     1 #include<stdio.h>
     2 #include<string.h>
     3 #include<algorithm>
     4 #include<queue>
     5 #include<vector>
     6 
     7 using namespace std;
     8 #define maxn 100010
     9 struct node {
    10     int x,y;
    11     node(int x=0,int y=0):x(x),y(y){};
    12 };
    13 int rk[maxn],d[maxn],p[maxn][30];
    14 vector<node>a[maxn];
    15 int n;
    16 void dfs(int u,int fa,int cnt) {
    17     rk[u]=cnt;
    18     p[u][0]=fa;
    19     int len=a[u].size();
    20     for (int i=0 ; i<len ; i++) {
    21         int x=a[u][i].x;
    22         if (x!=fa) {
    23             d[x]=d[u]+a[u][i].y;
    24             dfs(x,u,cnt+1);
    25         }
    26     }
    27 }
    28 void lca() {
    29     for (int i=1 ; i<=n ; i++ ) {
    30         for (int j=1 ; (1<<j)<=n ; j++) {
    31             p[i][j]=-1;
    32         }
    33     }
    34     for (int j=1 ; (1<<j)<=n ; j++) {
    35         for (int i=1 ; i<=n ; i++) {
    36             if (p[i][j-1]!=-1) p[i][j]=p[p[i][j-1]][j-1];
    37         }
    38     }
    39 }
    40 int query(int x,int y) {
    41     if (rk[x]<rk[y]) swap(x,y );
    42     int k;
    43     for (k=1 ; (1<<(1+k))<=rk[x] ; k++);
    44     for (int i= k; i>=0 ; i--) {
    45         if (rk[x]-(1<<i)>=rk[y]) x=p[x][i];
    46     }
    47     if (x==y) return x;
    48     for (int i= k; i>=0 ; i--) {
    49         if (p[x][i]!=-1 && p[x][i]!=p[y][i]){
    50             x=p[x][i];
    51             y=p[y][i];
    52         }
    53     }
    54     return p[x][0];
    55 }
    56 int  main() {
    57     int q,u,v,w;
    58     while(scanf("%d", &n)!=EOF) {
    59         for (int i = 1; i < n; i++) {
    60             scanf("%d%d%d", &u, &v, &w);
    61             a[u].push_back(node(v, w));
    62             a[v].push_back(node(u, w));
    63         }
    64         dfs(1, -1, 0);
    65         lca();
    66         scanf("%d", &q);
    67         while (q--) {
    68             scanf("%d%d", &u, &v);
    69             printf("%d
    ", d[u]+d[v]-2*d[query(u, v)]);
    70         }
    71     }
    72     return 0;
    73 }
    其中DFS(int u,int fa, int cnt)
    u表示当前节点 fa为他的父亲节点 cnt代表的是深度;
    int rk[maxn]记录深度 d[maxn] 记录节点 p[maxn][30]记录父亲节点的位置
    lca() 这个就是精髓所在了 第一步初始化p[i][j]=-1;
    第二步就是二进制优化了 p[i][j]=p[p[i][j-1]][j-1] 表示i+2^j=i+2^(j-1)+
    2^(j-1)
    前面都是预处理 第三步query(int x,int y) 求x,y的公共祖先。
    先判断深度,然后算出2^k <rk[x] 的k的最大值。
    if (rk[x]-(1<<i)>=rk[y]) x=p[x][i];将x的的深度向上回溯2^i
    使之更接近rk[y]

       for (int i= k; i>=0 ; i--) {
          if (p[x][i]!=-1 && p[x][i]!=p[y][i]){
             x=p[x][i];
             y=p[y][i];
          }
       }
       return p[x][0];

    后面就是无脑回溯到公共祖先位置。

    非常感谢月老的LCA倍增模板  

    以上就是我对LCA倍增算法的解析  
    如果读者还有不懂可以留言给我。


  • 相关阅读:
    [Javascript] Drawing Paths
    [Javascript] Drawing Paths
    [Whole Web] [AngularJS + Grunt] Using ng-html2js to Convert Templates into JavaScript
    [TypeScript] 1. Catching JavaScript Mistakes with TypeScript
    [TypeScript] 0.First Example
    [AngularJS] Introduction to angular-formly
    Runoob-Java:Java String 类
    Runoob-Java:Java Number & Math 类
    Runoob-Java:Java switch case
    Runoob-Java:Java 条件语句
  • 原文地址:https://www.cnblogs.com/qldabiaoge/p/8580449.html
Copyright © 2011-2022 走看看