洛谷 P3398 仓鼠找 sugar(树上倍增 lca 判断树中两条路径是否相交 结论)

33 篇文章 1 订阅
30 篇文章 0 订阅

仓鼠找 sugar

题目描述

小仓鼠的和他的基(mei)友(zi)sugar住在地下洞穴中,每个节点的编号为1~n。地下洞穴是一个树形结构。这一天小仓鼠打算从从他的卧室(a)到餐厅(b),而他的基友同时要从他的卧室(c)到图书馆(d)。他们都会走最短路径。现在小仓鼠希望知道,有没有可能在某个地方,可以碰到他的基友?

小仓鼠那么弱,还要天天被zzq大爷虐,请你快来救救他吧!

输入格式

第一行两个正整数n和q,表示这棵树节点的个数和询问的个数。

接下来n-1行,每行两个正整数u和v,表示节点u到节点v之间有一条边。

接下来q行,每行四个正整数a、b、c和d,表示节点编号,也就是一次询问,其意义如上。

输出格式

对于每个询问,如果有公共点,输出大写字母“Y”;否则输出“N”。

样例 #1

样例输入 #1

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

样例输出 #1

Y
N
Y
Y
Y

提示

__本题时限1s,内存限制128M,因新评测机速度较为接近NOIP评测机速度,请注意常数问题带来的影响。__

20%的数据 n<=200,q<=200

40%的数据 n<=2000,q<=2000

70%的数据 n<=50000,q<=50000

100%的数据 n<=100000,q<=100000

题意:

给定 n 个节点,节点之间总共连有 n - 1 条边,给出 最多 1e5 个询问,每个询问都判断 a ~ bc ~ d 两条路径是否相交相交输出 ‘Y’,否则 ‘N’。

思路:

多画几个图模拟一下,可以发现如下一个神奇的规律:

如果 a ~ bc ~ d 两条路径相交,那么一定有 a ~ b 路径的 LCAc ~ d 路径上,或者 c ~ d 路径的 LCAa ~ b 路径上

判断一个节点 x,是否在路径 s - t 上需要满足如下几个条件:(请记住,重要结论

depth[x] >= depth[lca(s, t)]	// x 位于 s、t 两点的 lca 相同高度或下方 

lca(s, x) = x || lca(t, x) = x;	// s、x  lca  x  t、x  lca  x

代码:

#define _CRT_SECURE_NO_WARNINGS 1
#include <bits/stdc++.h>

using namespace std;
//#define int long long
//#define map unordered_map
const int N = 1e5 + 10, M = N << 1;
int n, q;
int h[N], e[M], ne[M], idx;
int depth[N];
int fa[N][18];

void add(int a, int b)
{
	e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}

void bfs(int u)
{
	queue<int> q; q.push(u);
	while (q.size())
	{
		auto t = q.front(); q.pop();
		for (int i = h[t]; ~i; i = ne[i])
		{
			int j = e[i];
			if (depth[j] > depth[t] + 1)
			{
				depth[j] = depth[t] + 1;
				q.push(j);
				fa[j][0] = t;
				for (int k = 1; k <= 17; ++k)
				{
					fa[j][k] = fa[fa[j][k - 1]][k - 1];
				}
			}
		}
	}
}

int lca(int a, int b)
{
	if (depth[a] < depth[b]) swap(a, b);

	for (int k = 17; k >= 0; --k)
	{
		if (depth[fa[a][k]] >= depth[b]) a = fa[a][k];
	}

	if (a == b) return a;

	for (int k = 17; k >= 0; --k)
	{
		if (fa[a][k] != fa[b][k])
		{
			a = fa[a][k];
			b = fa[b][k];
		}
	}

	return fa[a][0];
}

void init(int root)
{
	memset(depth, 0x3f, sizeof depth);
	depth[0] = 0, depth[root] = 1;
	bfs(root);
}

bool jud(int a, int b, int c, int d)
{
	int p = lca(a, b);
	if (depth[p] >= depth[lca(c, d)] && (lca(c, p) == p || lca(d, p) == p))
		return true;
	return false;
}

signed main()
{
	int T = 1; //cin >> T;

	while (T--)
	{
		memset(h, -1, sizeof h);
		cin >> n >> q;
		int t = n - 1;
		int root = 0;
		for (int i = 0; i < t; ++i)
		{
			int u, v; scanf("%d%d", &u, &v);
			if (!i) root = u;
			add(u, v), add(v, u);
		}
		init(root);
		while (q--)
		{
			int a, b, c, d; scanf("%d%d%d%d", &a, &b, &c, &d);
			if (jud(a, b, c, d) || jud(c, d, a, b))
			{
				puts("Y");
			}
			else puts("N");
		}
	}

	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是用C++编写的算法,它使用了递归的方法,在二叉排序树中出任意两个不同节点的最近公共祖先: ```cpp #include <iostream> using namespace std; struct TreeNode { int val; TreeNode* left; TreeNode* right; }; TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) { if (!root) return NULL; // 如果根节点为空,返回NULL if (root->val > p->val && root->val > q->val) { return lowestCommonAncestor(root->left, p, q); // 如果根节点大于p和q的值,那么p和q在根节点的左子树中,递归左子树 } else if (root->val < p->val && root->val < q->val) { return lowestCommonAncestor(root->right, p, q); // 如果根节点小于p和q的值,那么p和q在根节点的右子树中,递归右子树 } else { return root; // 如果根节点的值介于p和q的值之间,那么根节点就是它们的最近公共祖先 } } int main() { TreeNode* root = new TreeNode{6, NULL, NULL}; root->left = new TreeNode{2, NULL, NULL}; root->right = new TreeNode{8, NULL, NULL}; root->left->left = new TreeNode{0, NULL, NULL}; root->left->right = new TreeNode{4, NULL, NULL}; root->left->right->left = new TreeNode{3, NULL, NULL}; root->left->right->right = new TreeNode{5, NULL, NULL}; root->right->left = new TreeNode{7, NULL, NULL}; root->right->right = new TreeNode{9, NULL, NULL}; TreeNode* p = root->left->right->left; TreeNode* q = root->left->right->right; TreeNode* lca = lowestCommonAncestor(root, p, q); cout << "Lowest Common Ancestor of " << p->val << " and " << q->val << " is " << lca->val << endl; return 0; } ``` 该程序的输出结果是: ``` Lowest Common Ancestor of 3 and 5 is 4 ``` 其中,root是二叉排序树的根节点,p和q是任意两个不同的节点,lca是它们的最近公共祖先。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值