【Gym-101908 L】Subway Lines【树上两条路径交】

题意:

给出一棵 N N N 个点的树, Q Q Q 组询问,每次询问给出四个点,组成树上的两条路径,求两条路径上的交点个数。 ( 5 ≤ N ≤ 1 0 5 , 1 ≤ Q ≤ 2 ∗ 1 0 4 ) (5\leq N\leq 10^5,1\leq Q\leq 2*10^4) (5N105,1Q2104)


思路:

比赛的时候看到这道题,想都没想就掏出链剖板子,区间加和区间和查询就过了。

赛后发现有些人是 LCA \text{LCA} LCA 做的,才发现原来 LCA \text{LCA} LCA 可以求路径交与路径并。

给出两条路径 ( a , b ) (a,b) (a,b) ( c , d ) (c,d) (c,d),四个点两两求 LCA \text{LCA} LCA,得到 x 1 = l c a ( a , c ) , x 2 = l c a ( a , d ) , x 3 = l c a ( b , c ) , x 4 = l c a ( b , d ) x_1=lca(a,c),x_2=lca(a,d),x_3=lca(b,c),x_4=lca(b,d) x1=lca(a,c),x2=lca(a,d),x3=lca(b,c),x4=lca(b,d)。再从这四个点中取出深度最大的两个点 p 1 , p 2 p_1,p_2 p1,p2

p 1 = ̸ p 2 p_1=\not p_2 p1≠p2,则两条路径一定有交,交点为 p 1 , p 2 p_1,p_2 p1,p2。若 p 1 = p 2 p_1=p_2 p1=p2,且 p 1 p_1 p1 的深度小于 l c a ( a , b ) lca(a,b) lca(a,b) 或者小于 l c a ( c , d ) lca(c,d) lca(c,d),则两条路径无交点,否则交点为 p 1 p_1 p1


代码:

#include <bits/stdc++.h>
#define __ ios::sync_with_stdio(0);cin.tie(0);cout.tie(0)
#define rep(i,a,b) for(int i = a; i <= b; i++)
#define LOG1(x1,x2) cout << x1 << ": " << x2 << endl;
#define LOG2(x1,x2,y1,y2) cout << x1 << ": " << x2 << " , " << y1 << ": " << y2 << endl;
#define LOG3(x1,x2,y1,y2,z1,z2) cout << x1 << ": " << x2 << " , " << y1 << ": " << y2 << " , " << z1 << ": " << z2 << endl;
typedef long long ll;
typedef double db;
const int N = 1e5+100;
const db EPS = 1e-9;
using namespace std;

int n,q,t,tot,head[N],f[N][25],d[N];
struct Edge{
	int to,next;
}e[2*N];

void add(int x,int y){
	e[++tot].to = y, e[tot].next = head[x], head[x] = tot;
}

void dfs(int x,int fa){
	f[x][0] = fa;
	for(int i = 1; (1<<i) <= d[x]; i++)
		f[x][i] = f[f[x][i-1]][i-1];
	for(int i = head[x]; i; i = e[i].next){
		int y = e[i].to;
		if(y == fa) continue;
		d[y] = d[x]+1; dfs(y,x);
	}
}

int lca(int x,int y){
	if(d[x] > d[y]) swap(x,y);
	for(int i = t; i >= 0; i--)
		if(d[f[y][i]] >= d[x]) y = f[y][i];
	if(x == y) return x;
	for(int i = t; i >= 0; i--)
		if(f[x][i] != f[y][i]) x = f[x][i], y = f[y][i];
	return f[x][0];
}

int main()
{
	scanf("%d%d",&n,&q);
	t = (int)(log(n)/log(2))+1;
	tot = 1;
	rep(i,1,n-1){
		int a,b; scanf("%d%d",&a,&b);
		add(a,b); add(b,a);
	}
	dfs(1,0);
	rep(i,1,q){
		int a,b,c,D; scanf("%d%d%d%d",&a,&b,&c,&D);
		int x[5];
		x[1] = lca(a,c), x[2] = lca(a,D), x[3] = lca(b,c), x[4] = lca(b,D);
		int p1 = 0,p2 = 0;
		rep(j,1,4)
			if(d[x[j]] > d[p1]) p2 = p1, p1 = x[j];
			else if(d[x[j]] > d[p2]) p2 = x[j];
		int h1 = lca(a,b), h2 = lca(c,D);
		if(p1 == p2){
			if(d[p1] < d[h1] || d[p1] < d[h2]) printf("0\n");
			else printf("1\n");
		}
		else{
			int ans = d[p1]+d[p2]-2*d[lca(p1,p2)]+1;
		 	printf("%d\n",ans);
		}
	}
	return 0;
}

LCA \text{LCA} LCA例题扩展:

题意: n n n 个点, n n n 条边,给出图上两点,求图上两点最短距离,保证图连通。 ( 1 ≤ n ≤ 1 0 5 ) (1\leq n\leq 10^5) (1n105)

思路: n n n 个点,图连通,则是在树上多加了一条边。因此对于图上两点来说,其最短距离要么经过多加的这条边,要么不经过这条边。

因此我们先用并查集处理出多加的这条边 ( u , v ) (u,v) (u,v),则对于 ( a , b ) (a,b) (a,b) 来说,其最短距离只有三种可能。一种是先在原树上求出 d i s ( a , b ) dis(a,b) dis(a,b),另一种是 d i s ( a , u ) + w ( u , v ) + d i s ( v , b ) dis(a,u)+w(u,v)+dis(v,b) dis(a,u)+w(u,v)+dis(v,b) d i s ( a , v ) + w ( u , v ) + d i s ( u , b ) dis(a,v)+w(u,v)+dis(u,b) dis(a,v)+w(u,v)+dis(u,b)。三种情况取最小值即可完成此题。

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Gene_INNOCENT

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值