题目描述
小F最近在研究蜂巢,如下图所示,这是一个蜂巢的横剖图,每一个格子都是一个正六边形,多个格子平铺构成一个无限大的平面。我们以中央的格子作为原点,按照下图规律,一圈一圈向外将每个格子都编上号。
小F想知道,如果一个蜜蜂当前在编号为x的格子处,它想到达编号为y的格子,最优情况下它最少需要经过多少个格子(包含起点终点)。蜜蜂在蜂巢里只能爬行,也就是说它每次只能爬到相邻的格子里。
你需要支持多组询问。
输入描述:
第一行,一个整数
T
(
1
≤
T
≤
100000
)
T\ (1 \leq T \leq 100000)
T (1≤T≤100000) ,表示有T组询问。
对于每组询问,输入一行。每行两个正整数
x
,
y
(
1
≤
x
,
y
≤
1
0
9
)
x,y\ (1 \leq x,y \leq 10^9)
x,y (1≤x,y≤109) ,表示两个格子的编号。
输出描述:
对于每组询问,输出一行。每行一个整数,表示两个格子之间的格子数量。
示例1
输入
6
3 6
1 12
12 18
12 23
48 60
42 54
输出
3
3
5
4
9
9
题目不难,记得cf有道类似的题,但是现场想到的编号转换特别难实现,因此没有写。这里补一个比较好写的做法。
如图所示建立坐标系。
观察发现,每一圈编号组成一个六边形,蓝色圈出的数字为每一圈中最大的数字,第
i
i
i 圈对应的数字为
3
i
(
i
−
1
)
+
1
3i(i-1)+1
3i(i−1)+1 ,可以通过二分
O
(
log
1000000000
)
O(\log\sqrt{1000000000})
O(log1000000000) 求出编号对应的圈数,然后根据编号与其所在圈最大数之差,从最大数所在坐标
O
(
1
)
O(1)
O(1) 旋转到编号所在坐标,如图中黄色箭头标出了编号
12
12
12 的坐标旋转过程。这样就完成了编号到坐标的转换。
设两个点的坐标分别为
(
x
1
,
y
1
)
(x_1,y_1)
(x1,y1) 和
(
x
2
,
y
2
)
(x_2,y_2)
(x2,y2) 。
令
d
x
=
x
1
−
x
2
d_x=x_1-x_2
dx=x1−x2 ,
d
y
=
y
1
−
y
2
d_y=y_1-y_2
dy=y1−y2 。
- 若 d y d_y dy 和 d y d_y dy 正负相同,则在减小 d x d_x dx 的同时可以让 d y d_y dy 减小,因此答案为 min ( ∣ d x ∣ , ∣ d y ∣ ) + 1 \min(|d_x|,|d_y|)+1 min(∣dx∣,∣dy∣)+1 。
- 若 d y d_y dy 和 d y d_y dy 正负不同,则答案为 ∣ d x ∣ + ∣ d y ∣ + 1 |d_x|+|d_y|+1 ∣dx∣+∣dy∣+1 。
#include <bits/stdc++.h>
using namespace std;
const int dx[] = {1, 0, -1, -1, 0, 1};
const int dy[] = {0, 1, 1, 0, -1, -1};
int T, num, x[2], y[2], ans;
vector<int> seq;
int main() {
for (int i = 0; i * (i - 1) * 3 + 1 <= 1000000000; i++)
seq.push_back(i * (i + 1) * 3 + 1);
scanf("%d", &T);
while (T--) {
memset(x, 0, sizeof(x));
for (int i = 0; i < 2; i++) {
scanf("%d", &num);
int p = lower_bound(seq.begin(), seq.end(), num) - seq.begin();
y[i] = -p, num = seq[p] - num;
for (int j = 0, l; j < 6 && num; j++) {
l = min(num, p), num -= l;
x[i] += dx[j] * l;
y[i] += dy[j] * l;
}
}
x[0] -= x[1], y[0] -= y[1];
if (x[0] * y[0] > 0) ans = abs(x[0]) + abs(y[0]) + 1;
else ans = max(abs(x[0]), abs(y[0])) + 1;
printf("%d\n", ans);
}
return 0;
}