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使用详解)

代码:


      #include<iostream>
      #include<stdio.h>
      #include<vector>
      using namespace std;
      int n;
      vector<int>v[100001];//存儿子标号
      int deep[100001];//每个点的深度
      int visitnum[200001];//遍历数是2*n-1
      int visitn[100001];//每个点的任意一个遍历数
      int vnum;
      int mins[200001][20];		//区间最小值
      int fa[100001];
      void visit(int m, int d) //遍历重编号
      {
      	vector<int>::iterator p;
      	deep[m] = d;
     	for (p = v[m].begin(); p != v[m].end(); p++)
      	{
     		if (fa[*p]>-1)continue;
      		fa[*p] = m;
      		visitnum[vnum++] = m;	//存入访问的第vnum个点是哪个点
     		visit(*p, d + 1);
      	}
      	visitn[m] = vnum;		//注意这2句的顺序
      	visitnum[vnum++] = m;
      }
      void rmq() //计算区间最小值
      {
     	for (int i = 1; i <= 2 * n - 1; i++)mins[i][0] = visitnum[i];
     	for (int j = 1; (1 << j) <= 2 * n - 1; j++)
      	{
     		for (int i = 1; i <= 2 * n - 1; i++)
      		{
      			mins[i][j] = mins[i][j - 1];
     			int k = i + (1 << (j - 1));
     			if (k <= 2 * n - 1 && deep[mins[i][j]] > deep[mins[k][j - 1]])
      				mins[i][j] = mins[k][j - 1];
      		}
      	}
      }
      int lca(int x, int y) //求最近公共祖先
      {
      	x = visitn[x], y = visitn[y];
     	if (x > y)x ^= y ^= x ^= y;
     	int j = 0;
     	while ((1 << j) <= y - x + 1)j++;
      	j--;
     	int min = mins[y + 1 - (1 << j)][j];
     	if (deep[min] > deep[mins[x][j]])min = mins[x][j];
     	return min;
      }
      int main()
      {
     	int q, x, y, k;
     	bool fl = false;
     	while (scanf("%d%d", &n, &q) != EOF)
      	{
     		if (fl)printf("\n");
      		fl = true;
      		vnum = 1;
     		for (int i = 1; i <= n; i++)
      		{
      			v[i].clear();	//初始化
      			fa[i] = -1;
      		}
     		for (int i = 1; i < n; i++)
      		{
     			scanf("%d%d", &x, &y);
      			v[x].insert(v[x].end(), y);//存入儿子的标号
      			v[y].insert(v[y].end(), x);//存入儿子的标号
      		}
      		fa[1] = 1;
     		visit(1, 1);
     		rmq();
     		while (q--)
      		{
     			scanf("%d%d%d", &x, &y, &k);
     			int lca1 = lca(x, y), lca2 = lca(x, k), lca3 = lca(y, k);
     			bool flag = (lca1 == lca2 && lca3 == k);
     			if (lca1 == lca3 && lca2 == k)flag = true;
     			if (flag)printf("YES\n");
     			else printf("NO\n");
      		}
      	}
     	return 0;
      }
  
 

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

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

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

评论(0

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

全部回复

上滑加载中

设置昵称

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

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

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