• 最近公共祖先


    tarjan算法:离线处理询问,核心思想是并查集。 如该图,现有4个询问:6-7,6-9,7-10,2-11

     

    实现:1.先把询问数翻倍,除了本身的询问,还要增加两个点反过来的询问,即7-6,9-6,10-7,11-2。为什么要这样?因为在询问时,不知道两个点的先后顺序。

    2.从根结点出发,做dfs遍历,遍历到第一个叶子结点时开始回退。每个结点回退前,查询该点有无询问,有则处理。回退时,进行并查集的合并操作。如上图,从1开始遍历,1-2-4-6,6准备回退前,查询有无6的询问,有6-7,6-9,看另一点是否访问,7和9均未访问,回退,1-2-4-6-4,此时fa(6)=4,继续遍历与4连接的点,1-2-4-6-4-7,此时查找有无关于7的询问,查询到7-6,6已访问,此时fa(6)=4,4即为7-6的最近公共祖先。fa(7)=4,继续回退,1-2-4-6-4-7-4,......,1-2-4-6-4-7-4-2-5-8-5-9,遍历到9时,回退前查询询问,有关于9的询问,9-6,此时6已遍历,查6此时的父亲点2即为9-6的最近公共祖先。

    代码如下:

     1 #include <vector>
     2 #include <cstdio>
     3 using namespace std;
     4 inline int read()
     5 {
     6     int x = 0, f = 0;
     7     char c = getchar();
     8     for (; c < '0' || c > '9'; c = getchar()) if (c == '-') f = 1;
     9     for (; c >= '0' && c <= '9'; c = getchar()) x = (x << 1) + (x << 3) + (c ^ '0');
    10     return f ? -x : x;
    11 }
    12 
    13 const int N = 5e5 + 7;
    14 
    15 int n, m, u, v, s;
    16 int tot = 0, st[N], to[N << 1], nx[N << 1], fa[N], ans[N], vis[N];
    17 struct note { int node, id; }; //询问以结构体形式保存
    18 vector<note> ques[N];
    19 
    20 inline void add(int u, int v) 
    21 { to[++tot] = v, nx[tot] = st[u], st[u] = tot; 
    22 }
    23 inline int getfa(int x) 
    24 { return fa[x] == x ? x : fa[x] = getfa(fa[x]); } 
    25 //并查集的getfa操作,路径压缩
    26 
    27 void dfs(int u, int from)
    28 //u代表目前所在的点,from代表u的父亲点 
    29 {
    30     for (int i = st[u]; i; i = nx[i]) //遍历与u相连的所有的边 
    31          if (to[i] != from) 
    32              dfs(to[i], u), fa[to[i]] = u; //将u的儿子合并到u
    33     int len = ques[u].size(); //处理与u有关的询问
    34     for (int i = 0; i < len; i++) 
    35     if (vis[ques[u][i].node]) 
    36          ans[ques[u][i].id] = getfa(ques[u][i].node); 
    37     //处理与u相关的第i个询问,如果对应的v已访问 
    38     //找此时v的father,即为u,v的最近公共祖先 
    39     vis[u] = 1; //访问完毕回溯
    40 }
    41 
    42 int main()
    43 {
    44     n = read(), m = read(), s = read();
    45     //n个点,m条询问,s为root 
    46     for (int i = 1; i < n; i++) //n-1条边 
    47     u = read(), v = read(), 
    48     add(u, v), add(v, u);
    49     for (int i = 1; i <= m; i++)  //m个询问 
    50     u = read(), v = read(), 
    51     ques[u].push_back((note){v, i}), 
    52     ques[v].push_back((note){u, i}); 
    53     //记下询问编号便于输出
    54     for (int i = 1; i <= n; i++) 
    55           fa[i] = i;
    56     dfs(s, 0); //从s出发,访问 
    57     for (int i = 1; i <= m; i++) 
    58     printf("%d
    ", ans[i]); //输出答案
    59     return 0;
    60 }

     

  • 相关阅读:
    NOI2015刷题记录
    [WC2013][UOJ58]糖果公园 莫队算法
    啦啦啦~
    完全平方数
    构建之法
    测试更新
    程序的测试
    程序的封装
    构建之法5.5-6-7章观后感
    给徐侃童鞋的一个汉堡
  • 原文地址:https://www.cnblogs.com/cutepota/p/12499407.html
Copyright © 2020-2023  润新知