【LCA】「StOI-1」树上询问

L i n k Link Link

l u o g u   P 6374 luogu\ P6374 luogu P6374

D e s c r i p t i o n Description Description

S a m p l e Sample Sample I n p u t Input Input 1 1 1

10 5
1 2
1 3
2 4
2 5
2 10
5 6
3 7
7 8
7 9
4 6 2
4 10 1
6 8 3
9 10 2
4 10 5

S a m p l e Sample Sample O u t p u t Output Output 1 1 1

7
0
1
4
0

S a m p l e Sample Sample I n p u t Input Input 2 2 2

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

S a m p l e Sample Sample O u t p u t Output Output 2 2 2

2
1
0

S a m p l e Sample Sample I n p u t Input Input 3 3 3

20 10
1 2
1 3
1 4
2 5
2 6
3 10
4 13
4 14
6 7
6 8
10 11
4 15
4 16
8 9
11 12
16 17
16 18
16 19
17 20
15 19 16
1 12 1
20 20 20
7 7 8
1 8 3
5 20 2
2 9 6
9 12 1
9 12 2
9 12 3

S a m p l e Sample Sample O u t p u t Output Output 3 3 3

4
16
20
0
0
5
2
10
2
1

H i n t Hint Hint

T r a i n Train Train o f of of T h o u g h t Thought Thought

其实这个不能取的点就只有 a a a c c c a a a b b b l c a lca lca)的的路径上的点以及a的子树(b也同理)
然后我们求出以1为根的A与B的LCA
可以看看这位大佬的 b l o g blog blog
然后分以下情况讨论

  1. L C A = c LCA = c LCA=c那么答案就是 n − ( 含 a 的 子 树 ) − ( 含 b 的 子 树 ) n - (含a的子树) - (含b的子树) n(a)b
  2. c c c a a a l c a lca lca 的路径上,答案是 n − a 和 不 在 c 所 在 的 子 树 的 节 点 ) n−a 和不在 c 所在的子树的节点 ) nac)
  3. c c c b b b l c a lca lca 的路径上,答案是 n − ( b 和 不 在 c 所 在 的 子 树 的 节 点 ) n−(b 和不在 c 所在的子树的节点 ) n(bc)
  4. c c c不在 a a a b b b的路径上则答案为0

C o d e Code Code

#include<iostream>
#include<cstdio>
#include<cmath>

using namespace std;

int fa[22][1000001];
int n, m, t, q, u, v;
int h[500001], dep[500001], num[500001];

struct node
{
	int to, next;
}w[1000001];

void add(int x, int y)
{
	w[++t] = (node){y, h[x]}; h[x] = t;
	w[++t] = (node){x, h[y]}; h[y] = t;
}

void dfs(int now, int fath)
{
	fa[0][now] = fath;
	dep[now] = dep[fath] + 1;
	for (int i = h[now]; i; i = w[i].next) { 
		if (w[i].to != fath) {
			dfs(w[i].to, now);
			num[now] += num[w[i].to];
		} 
	} 
	num[now]++;
}

int dis(int x, int y)
{
	if (x == y) return 0;
	for (int i = log2(n) + 1; i >= 0; --i)
		if (dep[fa[i][x]] > dep[y]) x = fa[i][x];
	return num[x];
}//求y的含有x的子树所包含的节点数


int LCA(int x, int y)
{
	if (dep[x] < dep[y]) swap(x, y);
	for (int i = log2(n) + 1; i >= 0; --i)
		if (dep[fa[i][x]] >= dep[y]) x = fa[i][x];
	if (x == y) return x;
	for (int i = log2(n) + 1; i >= 0; --i)
		if (fa[i][x] != fa[i][y]) 
		{
			x = fa[i][x];
			y = fa[i][y];
		}
	return fa[0][x];
}

bool check(int x, int y, int z)
{
	int k = LCA(x, z);
	return (((LCA(x, y) == y) || (LCA(y, z) == y)) && LCA(y, k) == k);
}

int main()
{
	scanf("%d%d", &n, &q);
	for (int i = 1; i < n; ++i)
	{
		scanf("%d%d", &u, &v);
		add(u, v);
	}
	dfs(1, 0);
	for (int i = 1; i <= 20; ++i)
		for (int j = 1; j <= n; ++j)
			fa[i][j] = fa[i - 1][fa[i - 1][j]];
	for (int i = 1; i <= q; ++i)
	{
		int a, b, c;
		scanf("%d%d%d", &a, &b, &c); 
		int lca = LCA(a, b);
		if (lca == c) printf("%d\n", n - dis(a, c) - dis(b, c));
		else if (check(a, c, lca)) 
			printf("%d\n", n - dis(a, c) - (n - num[c]));
		else if (check(b, c, lca)) 
			printf("%d\n", n - dis(b, c) - (n - num[c]));
		else printf("0\n");
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值