• 【lhyaaa】最近公共祖先LCA——倍增!!!


    高级的算法——倍增!!!

    根据LCA的定义,我们可以知道假如有两个节点xy,则LCA(x,y)是 x 到根的路 径与 y 到根的路径的交汇点,同时也是 x 和 y 之间所有路径中深度最小的节 点,所以,我们可以用遍历路径的方法求 LCA。

    但想想都知道啦,这种遍历的方法肯定too slow,最坏情况时可达到O(n),数据大点儿,就光荣TLE了。

    所以我们高级的化身——倍增算法就出现了!

    谈谈倍增——

    倍增简单来讲就是两个点跳到同一高度后,再一起往上跳,直到跳到一个共同的点,就能找到它们的最近公共祖先啦

    具体来说,分为以下几个步骤——

    首先,我们得找几个帮手——

    数组 解释
    f[][] 预处理倍增f[v][i]          
    d[][] 记录节点深度
    vis[][] 判重,以防搜到节点的爸爸
    head[][] 存图时用der~(不懂
    dis[][]

    一个节点到根的最短距离

    (有权值记得加权值)

    Step1:  预处理每个结点的深度和该节点的父亲节点

    我们用 d 数组来表示每个结点的深度,设节点 1 为根,d[1]=0,初始化 f[v][0]=u, 表示 v 的 20 的祖先节点为 u。

    tips:如果树是无根树时需要把它变成有根数(随便找个点就OK)

    Step2:  预处理倍增f[v][i],2的祖先也是该点 2j-1 祖先的祖先 

    核心: f[u][j] = f [f [u][j-1]][j-1] 

    不懂的盆友,我们来举个例子——

    我们有这样的一张图,我们对它进行预处理就会变成这样——

    *图源

    还不懂的,就手动模拟一波~

     Step3:  查询时,深度大的先往上跳,直至与深度小的点再同一层。若此时两节点相同直接返回此节点(在同一条链上),如果不同,就利用倍增的思想,同时让 x 和 y 向上找,直到找到深度相等且最小的 x 和 y 的祖先 x′,y′,满足 x′≠y′。此时他们的父结点即为 x 和 y 的最近公共祖先 LCA。 

    倍增解法是 LCA 问题的在线解法,整体时间复杂度是 O(VlogV+QlogV),其 中 Q 是总查询次数。

    看起来是不是还不错?那我们来看道题吧~

    谈谈题目—— 

     

    Description

    给定一棵n个点的树,Q个询问,每次询问点x到点y两点之间的距离。

    Input

    第一行一个正整数n,表示这棵树有n个节点;接下来n-1行,每行两个整数x,y表示x,y之间有一条连边;然后一个整数Q,表示有Q个询问;接下来Q行每行两个整数x,y表示询问x到y的距离。

    对于全部数据,1≤n≤105,1≤x,y≤n。

    Output 

    输出Q行,每行表示每次询问的答案。

    Example

    stdin 

    6

    1 2

    1 3

    2 4

    2 5

    3 6

    2

    2 6

    5 6

    stdout 

    3

    4

    *原题

    Thinking 

    说实话这题没什么好讲的叭,就是求LCA就OK啦,板子题耶!

    上代码——

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 int t,q,tot;
     4 int dis[1100010];
     5 int vis[1100010];
     6 struct node{
     7     int nxt,to;
     8 }e[1100010];
     9 int head[1100010];
    10 int f[1100010][20];
    11 int d[1100010];
    12 
    13 void add(int u, int v){
    14     e[++tot].to = v;
    15     e[tot].nxt=head[u];
    16     head[u] = tot;
    17 }
    18 
    19 void dfs(int u, int fa, int dep){ //预处理深度,倍增数组
    20     vis[u] = 1;
    21     f[u][0] = fa; //初始化记录父节点
    22     d[u] = dep;
    23     for(int j = 1; j <= 19; j++) f[u][j] = f[f[u][j-1]][j-1];
    24     for(int i = head[u]; i; i = e[i].nxt){
    25         int v = e[i].to;
    26         if(vis[v]) continue;
    27         dfs(v,u,dep+1);
    28     }
    29 }
    30 
    31 int lca(int x, int y){ //倍增求 LCA
    32     if(d[x] > d[y]) swap(x,y);
    33     for(int i = 19; i >= 0; i--){
    34         if(d[f[y][i]] >= d[x]) y = f[y][i]; //深度较深节点先往上跳至同一深度
    35         if(x == y) return x;
    36     }
    37     for(int i = 19; i >= 0; i--){
    38         if(f[x][i] != f[y][i]) x = f[x][i], y= f[y][i];
    39     }
    40     return f[x][0];
    41 }
    42 
    43 int main(){
    44     scanf("%d",&t);
    45     tot = 0;
    46     for(int i = 1; i < t; i++){
    47         int u,v;
    48         scanf("%d%d",&u,&v);
    49         add(u,v),add(v,u);
    50     }
    51     dfs(1,0,0);
    52     cin >> q;
    53     for(int i = 1; i <= q; i++){
    54         int x,y;
    55         scanf("%d%d",&x,&y);
    56         printf("%d
    ",d[x]+d[y]-2*d[lca(x,y)]); //两个节点到跟的距离减去重复计算的它们公共祖先到跟的距离
    57     }    
    58 }

    解释一下这个东西 d[x] + d[y] - 2 * d[lca(x,y)];

    画张图——

     

    d[x] 和 d[y] 就是红色那两条东西

    d[lca(x,y)] 就是蓝色那条

    d[x] + d[y] - 2*d[lca(x,y)] 就是绿色那条啦~

    当然这是没有权值得时候,我们默认深度差不多等于距离,但有了权值就不一样了。

    我们再来看一道板子题——

    Description

    给出n个点的一棵树,多次询问两点之间的最短距离。

    注意:边是双向的。

    Input

    第一行为两个整数n和m。n表示点数,m表示询问次数; 下来n-1行,每行三个整数x,y,k,表示点x和点y之间存在一条边长度为k;在接下来m行,每行两个整数x,y,表示询问点x到点y的最短距离。

    对于全部数据,2≤n≤104,1≤m≤2×104,0<k≤100,1≤x,y≤n。

    Output 

    输出m行。对于每次询问,输出一行。

    Example

    stdin1

    2 2 1 2 100 1 2 2 1

    stdout1

    100 100

    stdin2

    3 2 1 2 10 3 1 15 1 2 3 2

    stdout2 

    10 25

    *原题

     1 #include<bits/stdc++.h>
     2 using namespace std; 
     3 int n,m,q,tot;
     4 int dis[1100010];
     5 int vis[1100010];
     6 struct node{
     7     int nxt,to;
     8     int w;
     9 }e[1100010];
    10 int head[1100010];
    11 int f[1100010][20];
    12 int d[1100010];
    13 
    14 void add(int u, int v, int w){
    15     e[++tot].to = v;
    16     e[tot].w = w;
    17     e[tot].nxt=head[u];
    18     head[u] = tot;
    19 }
    20 
    21 void dfs(int u, int fa, int dep){
    22     vis[u] = 1;
    23     f[u][0] = fa;
    24     d[u] = dep;
    25     for(int j = 1; j <= 19; j++) f[u][j] = f[f[u][j-1]][j-1];
    26     for(int i = head[u]; i; i = e[i].nxt){
    27         int v = e[i].to;
    28         if(vis[v]) continue;
    29         dis[v] = dis[u] + e[i].w;
    30         dfs(v,u,dep+1);
    31     }
    32 }
    33 
    34 int lca(int x, int y){
    35     if(d[x] > d[y]) swap(x,y);
    36     for(int i = 19; i >= 0; i--){
    37         if(d[f[y][i]] >= d[x]) y = f[y][i];
    38         if(x == y) return x;
    39     }
    40     for(int i = 19; i >= 0; i--){
    41         if(f[x][i] != f[y][i]) x = f[x][i], y= f[y][i];
    42     }
    43     return f[x][0];
    44 }
    45 int main(){
    46     scanf("%d%d",&n,&m);
    47     for(int i = 1; i < n; i++){
    48         int u,v,w;
    49         scanf("%d%d%d",&u,&v,&w);
    50         add(u,v,w);
    51         add(v,u,w);
    52     }
    53     dfs(1,0,1);
    54     for(int i = 1; i <= m; i++){
    55         int x,y;
    56         scanf("%d%d",&x,&y);
    57         printf("%d
    ",dis[x]+dis[y]-2*dis[lca(x,y)]);
    58     }
    59 }

    注意到没有?

    这一道题是有权值的,所以最后输出的时候输出的是 dis[x] + dis[y] - 2 * dis[lca(x,y)]

    总结一下 

    其实LCA还有别的不同的求法,下次在和你们讲吧(其实是我还没学会) 

    这次就先到这儿吧~

    拜拜~

    (如果文章有不对的地方,请指出,谢谢啦^=^)

     
  • 相关阅读:
    02
    01
    Redis、Mongo
    Django
    Django
    Django
    Django
    7.2
    Django
    contenttypes
  • 原文地址:https://www.cnblogs.com/lhyaaa/p/13392440.html
Copyright © 2020-2023  润新知