• 倍增法求最近公共祖先(LCA)


    最近公共祖先的理解:

    在一颗有根树中,给定两个节点,找到他们两个最近的公共祖先(为保证统一性,结点本身也为其祖先)

    解决方法:

    一、向上标记法       O(n)

    从一个点向他的根节点走,经过的所有点都标记一下,再走第二个点,如果走到被标记的点即位二者的最近公共祖先

    二、倍增法

    预处理 O(nlogn)

    01
    f[i,j]数组表示:节点i向上跳2^j步到达的点
    if(j==0)    f[i,j]=i的父节点
    if(j>0)     f[i,j]=f[f[i,j-1],j-1]
    02
    depth[i]数组表示:节点i的层数,即到根节点的距离+1

    查询 O(logn)

    【1】 让两个点跳到同一层

    【2】 让两个点同时跳,直到跳到两个点的最近公共祖先的下一层

    例题:祖孙询问

    给定一棵包含 n个节点的有根无向树,节点编号互不相同,但不一定是 1∼n。有 m

    个询问,每个询问给出了一对节点的编号 x 和 y,询问 x 与 y的祖孙关系。

    输入格式

    输入第一行包括一个整数 表示节点个数;

    接下来 n行每行一对整数 a 和 b,表示 a 和 b 之间有一条无向边。如果 b 是 −1,那么 a

    就是树的根;

    第 n+2行是一个整数 m表示询问个数;

    接下来 m行,每行两个不同的正整数 x 和 y,表示一个询问。

    输出格式

    对于每一个询问,若 x是 y 的祖先则输出 1,若 y 是 x 的祖先则输出 2,否则输出 0

    数据范围

    1≤n,m≤4×104
    1≤每个节点的编号≤4×104

    输入样例:

    1. 10
    2. 234 -1
    3. 12 234
    4. 13 234
    5. 14 234
    6. 15 234
    7. 16 234
    8. 17 234
    9. 18 234
    10. 19 234
    11. 233 19
    12. 5
    13. 234 233
    14. 233 12
    15. 233 13
    16. 233 15
    17. 233 19

    输出样例:

    1. 1
    2. 0
    3. 0
    4. 0
    5. 2

    AC_code

    1. #include
    2. #include
    3. #include
    4. #include
    5. using namespace std;
    6. const int N=4e4+10;
    7. int n,m;
    8. int h[N],e[2*N],ne[2*N],idx;
    9. int f[N][16];//i号结点向上跳2^j步到达的结点编号
    10. int depth[N];//i号结点的层数
    11. void add(int a,int b)
    12. {
    13. e[idx]=b,ne[idx]=h[a],h[a]=idx++;
    14. }
    15. void bfs(int root)
    16. {
    17. memset(depth,0x3f3f3f,sizeof depth);
    18. depth[0]=0,depth[root]=1;
    19. // 哨兵depth[0] = 0: 如果从i开始跳2^j步会跳过根节点
    20. // fa[fa[j][k-1]][k-1] = 0
    21. // 那么fa[i][j] = 0 depth[fa[i][j]] = depth[0] = 0
    22. queue<int> q;
    23. q.push(root);
    24. while(!q.empty())
    25. {
    26. int t=q.front();
    27. q.pop();
    28. for(int i=h[t];~i;i=ne[i])
    29. {
    30. int j=e[i];
    31. if(depth[j]>depth[t]+1)
    32. {
    33. depth[j]=depth[t]+1;
    34. q.push(j);
    35. f[j][0]=t;
    36. for(int k=1;k<=15;k++)
    37. {
    38. f[j][k]=f[f[j][k-1]][k-1];
    39. }
    40. }
    41. }
    42. }
    43. }
    44. int lca(int a,int b)
    45. {
    46. if(depth[a]swap(a,b);
    47. //让两个点跳到同一层
    48. for(int k=15;k>=0;k--)
    49. {
    50. if(depth[f[a][k]]>=depth[b])
    51. a=f[a][k];
    52. }
    53. //让两个点同时跳,直到跳到两个点的最近公共祖先的下一层
    54. if(a==b) return a;
    55. for(int k=15;k>=0;k--)
    56. {
    57. if(f[a][k]!=f[b][k])
    58. {
    59. a=f[a][k];
    60. b=f[b][k];
    61. }
    62. }
    63. return f[a][0];
    64. }
    65. int main()
    66. {
    67. cin>>n;
    68. int root=0;
    69. memset(h,-1,sizeof h);
    70. for(int i=1;i<=n;i++)
    71. {
    72. int a,b;
    73. scanf("%d%d",&a,&b);
    74. if(b==-1) root=a;
    75. else
    76. {
    77. add(a,b);
    78. add(b,a);
    79. }
    80. }
    81. bfs(root);//预处理出f[][],depth[]数组
    82. cin>>m;
    83. while (m -- )
    84. {
    85. int a,b;
    86. scanf("%d%d",&a,&b);
    87. int q=lca(a,b);
    88. if(q==a) cout<<1<
    89. else if(q==b) cout<<2<
    90. else cout<<0<
    91. }
    92. return 0;
    93. }

  • 相关阅读:
    通过FNN算法进行特征组合的商品推荐详细教程 有代码+数据
    【第二章 数据的表示和运算】d1
    缓存热key问题
    NLP 03(LSTM)
    洛谷刷题笔记 确定进制
    在浏览器中运行 TensorFlow.js 来训练模型并给出预测结果(Iris 数据集)
    01、前言
    抖音预约服务小程序开发:前端与后端技术的完美融合
    JavaSE入门--初始Java
    AOT漫谈专题(第五篇): 如何劫持.NET AOT编译器 进行源码级调试
  • 原文地址:https://blog.csdn.net/weixin_62224014/article/details/127579499