2023 hdu 第1场 1001 Hide-And-Seek Game

Problem Description

在一棵树上,树的边权为 1 1 1 S e r e n a d e Serenade Serenade R h a p s o d y Rhapsody Rhapsodya 从各自的起点 S a S_a Sa , S b S_b Sb 向着终点 T a T_a Ta , T b T_b Tb 做往返运动。

S e r e n a d e Serenade Serenade R h a p s o y a Rhapsoya Rhapsoya 在节点上的最早相遇节点。

Input

第一行输入一个整数 t t t ( 1 ≤ t ≤ 500 ) (1 \le t \le 500) (1t500) ,表示测试组数。

接下来一行输入两个整数 n , m n,m n,m ( 2 ≤ n , m ≤ 3 × 1 0 3 ) (2 \le n,m \le 3 \times 10^3) (2n,m3×103),表示的树的节点个数和查询次数。

接下来 n − 1 n-1 n1 行每行输入两个整数 u , v u,v u,v ( 1 ≤ u , v ≤ n , u ≠ v ) (1 \le u,v \le n,u\not= v) (1u,vn,u=v) ,表示 u , v u,v u,v 之间存在一条边。

接下来 m m m 每行输入四个整数 S a , T a , S b , T b S_a,T_a,S_b,T_b Sa,Ta,Sb,Tb ( 1 ≤ S a , T a , S b , T b ≤ n , S a ≠ T a , S b ≠ T b ) (1 \le S_a,T_a,S_b,T_b \le n,S_a \not= T_a,S_b \not= T_b) (1Sa,Ta,Sb,Tbn,Sa=Ta,Sb=Tb) ,表示 S e r e n a d e Serenade Serenade R h a p s o d y Rhapsody Rhapsodya 各自的起点和终点。

数据保证不超过 20 20 20 n , m n,m n,m 超过 400 400 400

Output

对于每次查询,输出对应节点编号,如果不会相遇则输出 − 1 -1 1

Solution

下文中 S e r e n a d e Serenade Serenade R h a p s o y a Rhapsoya Rhapsoya 都视作 a a a , b b b

首先如果 a a a b b b 要相交,那么 a a a 起点到终点的路径必然和 b b b 起点到终点的路径相交。而对于相交路径,由于 n n n m m m 只有 3 × 1 0 3 3 \times 10^3 3×103 ,我们可以直接暴力在 a a a b b b 的路径上的点各自 + 1 +1 +1 ,那么此时我们只需考虑路径上的权值为 2 2 2 的点即可。

那么对于一个点是否能相遇以及求 a a a b b b 的最早相遇步数,我们进行分类讨论,设 a l 0 , a l 1 al_0,al_1 al0,al1 b l 0 , b l 1 bl_0,bl_1 bl0,bl1 a a a b b b 起点和终点到交点距离, k 1 , k 2 ∈ N + k_1,k_2 \in N^+ k1,k2N+,发现相遇只有 4 4 4 种情况:

· a a a 从起点走出与 b b b 从起点走出相遇。 2 k 1 × ( a l 0 + a l 1 ) + a l 0 = 2 k 2 × ( b l 0 + b l 1 ) + b l 0 2k_1 \times (al_0+al_1) + al_0 = 2k_2 \times (bl_0+bl_1) + bl_0 2k1×(al0+al1)+al0=2k2×(bl0+bl1)+bl0

· a a a 从起点走出与 b b b 从终点走出相遇。 2 k 1 × ( a l 0 + a l 1 ) + a l 0 = ( 2 k 2 − 1 ) × ( b l 0 + b l 1 ) + b l 1 2k_1 \times (al_0+al_1) + al_0 = (2k_2-1) \times (bl_0+bl_1) + bl_1 2k1×(al0+al1)+al0=(2k21)×(bl0+bl1)+bl1

· a a a 从终点走出与 b b b 与起点走出相遇。 ( 2 k 1 − 1 ) × ( a l 0 + a l 1 ) + a l 1 = 2 k 2 × ( b l 0 + b l 1 ) + b l 0 (2k_1-1) \times (al_0+al_1) + al_1 = 2k_2 \times (bl_0+bl_1) + bl_0 (2k11)×(al0+al1)+al1=2k2×(bl0+bl1)+bl0

· a a a 从终点走出与 b b b 与终点走出相遇。 ( 2 k 1 − 1 ) × ( a l 0 + a l 1 ) + a l 1 = ( 2 k 2 − 1 ) × ( b l 0 + b l 1 ) + b l 1 (2k_1-1) \times (al_0+al_1) + al_1 = (2k_2-1) \times (bl_0+bl_1) + bl_1 (2k11)×(al0+al1)+al1=(2k21)×(bl0+bl1)+bl1

对于如何计算最少步数和是否相交,我们只需 e x g c d exgcd exgcd 算出 k 1 k_1 k1 k 2 k_2 k2 的值,如何算对应最小整数即可。

复杂度分析: O ( 20 n m l o g n ) O(20nmlogn) O(20nmlogn) ,表面一算复杂度甚至可能会到 2 × 1 0 9 2 \times 10^9 2×109,但是观察到 e x g c d exgcd exgcd 实际并不会达到 l o g n logn logn ,虽然还带有可能 5 5 5 倍的常数(?),但是还是能神秘地通过此题。。。

Code

#include <bits/stdc++.h>
#define endl '\n'
using namespace std;
const int N = 3e3 + 10, M = 6e3 + 10;
int meet_point, step;//最优相遇交点和步数
vector<int>a(2), b(2), al(2), bl(2);
int acs[N];
int head[N], e[M], ne[M], idx;
int fa[N], dep[N], siz[N], wson[N], top[N];
void clear(int n) {
	idx = 0;
	for (int i = 0; i <= n + 5; i++) head[i] = -1, wson[i] = 0;
}
inline void addedge(int a, int b) {
	e[idx] = b, ne[idx] = head[a], head[a] = idx++;
}
int exgcd(int a, int b, int& x, int& y) {
	if (!b) {
		x = 1, y = 0;
		return a;
	}
	int d = exgcd(b, a % b, y, x);
	y -= (a / b) * x;
	return d;
}
//树剖LCA
void dfs1(int u, int father) {
	dep[u] = dep[father] + 1, fa[u] = father, siz[u] = 1;
	for (int i = head[u]; i != -1; i = ne[i]) {
		int v = e[i];
		if (v == father)continue;
		dfs1(v, u);
		if (siz[v] > siz[wson[u]])wson[u] = v;
		siz[u] += siz[v];
	}
}
void dfs2(int u, int chead) {
	top[u] = chead;
	if (wson[u])dfs2(wson[u], chead);
	for (int i = head[u]; i != -1; i = ne[i]) {
		int v = e[i];
		if (v == fa[u] || v == wson[u])continue;
		dfs2(v, v);
	}
}
int lca(int a, int b) {
	while (top[a] != top[b]) {
		if (dep[top[a]] < dep[top[b]])b = fa[top[b]];
		else a = fa[top[a]];
	}
	return dep[a] < dep[b] ? a : b;
}
void add_point(int u, int father) {//在u到father路径上的点+1
	while (u != father) {
		acs[u]++;
		u = fa[u];
	}
}
int dis(int x, int y) {//求x到y的距离
	int anc = lca(x, y);
	return dep[x] + dep[y] - 2 * dep[anc];
}
void judge_meet(int cross, int left, int right) {//判断是否相遇并统计答案
	int x0, y0, c, d, dx, dy;
	int lena = 2 * (al[0] + al[1]), lenb = 2 * (bl[0] + bl[1]);
	d = exgcd(lena, lenb, x0, y0);
	dx = lenb / d, dy = lena / d;
	c = right - left;
	if (c % d == 0) {
		int x = x0 * c / d, y = y0 * c / d;
		x = (x % dx + dx) % dx;
		y = (y % dy + dy) % dy;
		y -= dy;
		int val = min(lena * x + left, -lenb * y + right);
		if (step > val)meet_point = cross, step = val;
	}
}
void get_ans(int cross) {
	al[0] = dis(a[0], cross), al[1] = dis(a[1], cross);//al[0],al[1]表示a点起点终点到交点的距离
	bl[0] = dis(b[0], cross), bl[1] = dis(b[1], cross);//bl[0],bl[1]表示b点起点终点到交点的距离
	judge_meet(cross, al[0], bl[0]);
	judge_meet(cross, al[0], bl[0] + 2 * bl[1]);
	judge_meet(cross, al[0] + 2 * al[1], bl[0] + 2 * bl[1]);
	judge_meet(cross, al[0] + 2 * al[1], bl[0]);
}
void solve() {
	int n, m;
	cin >> n >> m;
	clear(n);
	for (int i = 1; i <= n - 1; i++) {
		int u, v;
		cin >> u >> v;
		addedge(u, v), addedge(v, u);
	}
	dfs1(1, 0);
	dfs2(1, 1);
	while (m--) {
		meet_point = -1;
		step = 2e9;
		for (int i = 1; i <= n; i++)acs[i] = 0;
		cin >> a[0] >> a[1] >> b[0] >> b[1];
		int x = lca(a[0], a[1]), y = lca(b[0], b[1]);
		add_point(a[0], x), add_point(a[1], x), acs[x]++;
		add_point(b[0], y), add_point(b[1], y), acs[y]++;
		for (int i = 1; i <= n; i++) if (acs[i] >= 2)get_ans(i);
		cout << meet_point << endl;
	}
}
signed main() {
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	int t;
	cin >> t;
	while (t--)solve();
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值