Borrow Classroom (LCA 倍增 )

4 篇文章 0 订阅

题目连接    

题意:
         一个N 个节点的树 N-1条边, 1号节点是根节点,Q次询问,每次询问3个整数 
         A B C 3个人(3个整数代表3个人所处的位置),
        要求A 在文件交到根节点前进行拦截 
        如果A C同时到1则算失败 
    数据范围
        3s 
        最多5组样例 
        n q <= 1e5
        时间分析可以用静态的Tarjan来求LCA 和倍增 求LCA
        离线算法 用Tarjan可能会快一点都试试吧 
    思路: 
         1) A到1的距离 要小于( B到 C的距离 + C到 1的距离) 
        2) 如果 A到1的距离 等于( B到 C的距离 + C到 1的距离)要判断是否在中途相遇 即LCA(a,c) != 1 
        3)A到1 的距离是 A的树深,
        4)B到C的距离可以用LCA(a, b)来求,dis[a] + dis[b] - 2 * dis[LCA(a,b)]
        5) C到1的距离是 C的树深 

LCA 倍增AC:

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 1e5 + 5;
int head[MAXN], cnt;
int depth[MAXN], fa[MAXN][30];
struct Edge{
	int to, dis, next;
}edge[MAXN << 1];
int n, q;
void add_edge(int u, int v, int dis) {
	edge[++cnt].to = v;
	edge[cnt].dis = dis;
	edge[cnt].next = head[u];
	head[u] = cnt; 
}
void init () {
	cnt = 1;
	memset(head, 0, sizeof(head)); 
	memset(depth, 0, sizeof(depth)); 
	memset(fa, 0, sizeof(fa)); 
}
void DFS(int now, int pre) {
	depth[now] = depth[pre] + 1;
	fa[now][0] = pre;
	for(int i = 1; (1 << i) <= depth[now]; ++i) {
		fa[now][i] = fa[fa[now][i - 1]][i - 1];
	}
	for (int i = head[now]; i; i = edge[i].next) {
		if(edge[i].to != pre) 
			DFS(edge[i].to, now);
	}
}
int LCA(int a, int b) {
	if(depth[a] < depth[b]) swap(a, b);
	int dep = depth[a] - depth[b];
	for (int i = 0; (1 << i) <= dep; ++i) {
		if((1 << i) & dep) {
			a = fa[a][i];
		}
	}
	if(a == b) return a;
	for (int i = log2(depth[a]); i >= 0; --i) {
		//如果父亲不同就向上跳, 如果父亲相同就减小距离再比较,直到不相同在跳。 
		if (fa[a][i] != fa[b][i]) {
			a = fa[a][i];
			b = fa[b][i];	
		}
	}
	return fa[a][0];
}
int distant(int a, int b) {
	int temp = LCA(a, b);
 	return depth[a] + depth[b] - 2 * depth[temp];
}
int main() {	
	int T;
	scanf("%d", &T);
	while(T--) {
		init(); 
		cin >> n >> q;
		int x, y;
		for(int i = 1; i < n; ++i) {
			scanf("%d%d", &x, &y);
			add_edge(x, y, 0);
			add_edge(y, x, 0);
		}
		int A, B, C;
		DFS(1, 1);
		for(int i = 1; i <= q; ++i) {
			scanf("%d%d%d", &A, &B, &C);
			int dis_BC = distant(B, C);
			int dis_A1 = depth[A]; 
			int dis_C1 = depth[C];
			bool flag = 0;
			if(dis_A1 < dis_BC + dis_C1) flag = 1;
			else if ((dis_A1 == dis_BC + dis_C1) && LCA(A, C) != 1) flag = 1;
			
			if(flag) printf("YES\n");
			else printf("NO\n");
		}
	}
	return 0;
}

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值