CSU 1079: 树上的查询 (LCA最近公共祖先问题)

举报
用户已注销 发表于 2021/11/19 05:40:15 2021/11/19
【摘要】 题目: Description 现有一棵有N个顶点的树,顶点的标号分别为1, 2, …, N。对于每个形如a b k的询问,你需要回答在从点a到点b的路径上是否包含点k。 Input 输入包含多组测试数据。 对于每组测试数据,第一行包含两个正整数N, Q (1<=N, Q<=10^5),分别表示这棵树一共有...

题目:

Description

现有一棵有N个顶点的树,顶点的标号分别为1, 2, …, N。对于每个形如a b k的询问,你需要回答在从点a到点b的路径上是否包含点k。

Input

输入包含多组测试数据。

对于每组测试数据,第一行包含两个正整数N, Q (1<=N, Q<=10^5),分别表示这棵树一共有N个顶点,接下来你需要回答Q个询问。接下来N-1行中,第i行描述了这棵树的第i条边:包含两个正整数xi、yi,表示点xi和点yi之间有一条无向边。再接下来一共Q行,每行均包含三个正整数a、b、k (1<=a, b, k<=N),表示你需要回答在从点a到点b的路径上是否包含点k。

Output

对于每组测试数据,你需要按顺序依次回答每个询问。对于任意一个询问a b k,如果点a到点b的路径上包含点k,则输出“YES”(不包括引号),否则输出“NO”(不包括引号)。每组数据结尾输出一个空行。

Sample Input

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

Sample Output

YES
YES
NO
YES

Hint

       由于数据量较大,推荐使用scanf/printf。


就是简单的LCA问题,关于LCA参考我的另外一篇博客:HDU 2586 How far away?(LCA使用详解)

代码:


  
  1. #include<iostream>
  2. #include<stdio.h>
  3. #include<vector>
  4. using namespace std;
  5. int n;
  6. vector<int>v[100001];//存儿子标号
  7. int deep[100001];//每个点的深度
  8. int visitnum[200001];//遍历数是2*n-1
  9. int visitn[100001];//每个点的任意一个遍历数
  10. int vnum;
  11. int mins[200001][20]; //区间最小值
  12. int fa[100001];
  13. void visit(int m, int d) //遍历重编号
  14. {
  15. vector<int>::iterator p;
  16. deep[m] = d;
  17. for (p = v[m].begin(); p != v[m].end(); p++)
  18. {
  19. if (fa[*p]>-1)continue;
  20. fa[*p] = m;
  21. visitnum[vnum++] = m; //存入访问的第vnum个点是哪个点
  22. visit(*p, d + 1);
  23. }
  24. visitn[m] = vnum; //注意这2句的顺序
  25. visitnum[vnum++] = m;
  26. }
  27. void rmq() //计算区间最小值
  28. {
  29. for (int i = 1; i <= 2 * n - 1; i++)mins[i][0] = visitnum[i];
  30. for (int j = 1; (1 << j) <= 2 * n - 1; j++)
  31. {
  32. for (int i = 1; i <= 2 * n - 1; i++)
  33. {
  34. mins[i][j] = mins[i][j - 1];
  35. int k = i + (1 << (j - 1));
  36. if (k <= 2 * n - 1 && deep[mins[i][j]] > deep[mins[k][j - 1]])
  37. mins[i][j] = mins[k][j - 1];
  38. }
  39. }
  40. }
  41. int lca(int x, int y) //求最近公共祖先
  42. {
  43. x = visitn[x], y = visitn[y];
  44. if (x > y)x ^= y ^= x ^= y;
  45. int j = 0;
  46. while ((1 << j) <= y - x + 1)j++;
  47. j--;
  48. int min = mins[y + 1 - (1 << j)][j];
  49. if (deep[min] > deep[mins[x][j]])min = mins[x][j];
  50. return min;
  51. }
  52. int main()
  53. {
  54. int q, x, y, k;
  55. bool fl = false;
  56. while (scanf("%d%d", &n, &q) != EOF)
  57. {
  58. if (fl)printf("\n");
  59. fl = true;
  60. vnum = 1;
  61. for (int i = 1; i <= n; i++)
  62. {
  63. v[i].clear(); //初始化
  64. fa[i] = -1;
  65. }
  66. for (int i = 1; i < n; i++)
  67. {
  68. scanf("%d%d", &x, &y);
  69. v[x].insert(v[x].end(), y);//存入儿子的标号
  70. v[y].insert(v[y].end(), x);//存入儿子的标号
  71. }
  72. fa[1] = 1;
  73. visit(1, 1);
  74. rmq();
  75. while (q--)
  76. {
  77. scanf("%d%d%d", &x, &y, &k);
  78. int lca1 = lca(x, y), lca2 = lca(x, k), lca3 = lca(y, k);
  79. bool flag = (lca1 == lca2 && lca3 == k);
  80. if (lca1 == lca3 && lca2 == k)flag = true;
  81. if (flag)printf("YES\n");
  82. else printf("NO\n");
  83. }
  84. }
  85. return 0;
  86. }

文章来源: blog.csdn.net,作者:csuzhucong,版权归原作者所有,如需转载,请联系作者。

原文链接:blog.csdn.net/nameofcsdn/article/details/79775935

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。