LCA的题

Problem - G1 - Codeforces

题意:给定一个有 n 个点的无向连通图,且保证无环,要求对输入的 m 个点询问是否存在简单路径,简单路径就是通过路径上每个点,并保证每个点只经过一次。

思路:先预处理每个节点的深度,想象一条链挂在某个节点上,找到一个最深的点 mx 在找到一个不在 mx 祖先节点上的 最深节点 mx1,然后遍历输入的 m 个节点,对每个节点 x 查询 他分别和 mx 与 mx1 的最近公共祖先 LCA。 在求得 mx 和 mx1的 LCA为fa

有两种情况 ,如图

a)若 x 为  mx 的祖先节点 ,例如 x 为 7 分别对 mx 和 mx1 求 LCA ,LCA(x, mx) =x   LCA(x, mx1) = fa,

b) 若 x 为  mx1 的祖先节点 ,例如 x 为 2 分别对 mx 和 mx1 求 LCA ,LCA(x, mx) = fa  LCA(x, mx1) = x,

代码

#include <bits/stdc++.h>
#define oo 0x3f3f3f3f
#define ll long long
#define IO ios::sync_with_stdio(0);cin.tie(0)
#define rep(i,a,n) for (ll i=a;i<=n;i++)
#define per(i,a,n) for (ll i=a;i>=n;i--)
#define fi first
#define se second
const int N = 2e5 + 10, M = 2 * N;
using namespace std;
typedef pair<ll, ll>  pll;
typedef pair<int, int> pii;
#define deb(i,x) if(int i==x) int k = 1; 
#define  pb push_back
#define all(x) x.begin(),x.end()
ll lowbit(ll x) { return x & -x; }
ll gcd(ll a, ll b) { return b ? gcd(b, a % b) : a; }
ll qmi(ll a, ll b, ll mod) {
	ll res = 1; while (b) { if (b & 1) res = res * a % mod; a = a * a % mod; b >>= 1; }
	return res;
}

vector<int> e[N];
int depth[N] = { 0 };
int p[N][30] = { 0 };

void dfs(int u, int fa)
{
	p[u][0] = fa;
	for (int i = 1; (1 << i) <= depth[u]; i++)
	{
		p[u][i] = p[p[u][i - 1]][i - 1];
	}
	for (auto x : e[u])
	{
		if (x != fa)
			dfs(x, u);
	}
}
int lca(int a, int b)
{
	if (depth[a] < depth[b])swap(a, b);

	int t = 25;
	while (depth[a] > depth[b])
	{
		if (depth[a] - depth[b] >= (1 << t))
			a = p[a][t];
		t--;
	}

	if (a == b)return a;

	for (int k = 25; k >= 0; k--)
	{
		if (p[a][k] != p[b][k])
			a = p[a][k], b = p[b][k];
	}

	return p[a][0];
}
set<int> s;
bool solve(int mx, int mx1)
{
	bool f = true;
	int fa = lca(mx, mx1);
	for (auto x : s)
	{
		bool k1 = lca(x, mx1) == fa && lca(x, mx) == x;
		bool k2 = lca(x, mx1) == x && lca(x, mx) == fa;
		if (!(k1 || k2))
		{
			f = false;
			break;
		}
	}
	return f;
}
void get_dep(int u, int p, int dep)
{
	depth[u] = dep;
	for (auto x : e[u])
	{
		if (x == p)continue;
		get_dep(x, u, dep + 1);
	}
}
int main()
{
	IO;

#ifndef ONLINE_JUDGE
	freopen("in.txt", "r", stdin);
#endif

	int n; cin >> n;
	for (int i = 1; i < n; i++)
	{
		int u, v; cin >> u >> v;
		e[u].push_back(v);
		e[v].push_back(u);
	}
	int q; cin >> q;
	get_dep(1, 0, 1);
	dfs(1, 1);
	while (q--)
	{
		int k; cin >> k;
		s.clear();
		int mx = 0, mx1 = 0;
		rep(i, 1, k)
		{
			int x; cin >> x;
			if (depth[x] > depth[mx])
				mx = x;
			s.insert(x);
		}
		for (auto x : s)
		{
			if (depth[x] > depth[mx1] && lca(x, mx) != x)
			{
				mx1 = x;
			}
		}
		if (solve(mx, mx1))cout << "YES\n";
		else cout << "NO\n";
	}

	return 0;
}

Problem - 2874

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值