2021ICPC河南省赛 B.Honeycomb

题目链接

题目描述
小F最近在研究蜂巢,如下图所示,这是一个蜂巢的横剖图,每一个格子都是一个正六边形,多个格子平铺构成一个无限大的平面。我们以中央的格子作为原点,按照下图规律,一圈一圈向外将每个格子都编上号。
在这里插入图片描述

小F想知道,如果一个蜜蜂当前在编号为x的格子处,它想到达编号为y的格子,最优情况下它最少需要经过多少个格子(包含起点终点)。蜜蜂在蜂巢里只能爬行,也就是说它每次只能爬到相邻的格子里。
你需要支持多组询问。
输入描述:
第一行,一个整数 T   ( 1 ≤ T ≤ 100000 ) T\ (1 \leq T \leq 100000) T (1T100000) ,表示有T组询问。
对于每组询问,输入一行。每行两个正整数 x , y   ( 1 ≤ x , y ≤ 1 0 9 ) x,y\ (1 \leq x,y \leq 10^9) x,y (1x,y109) ,表示两个格子的编号。
输出描述:
对于每组询问,输出一行。每行一个整数,表示两个格子之间的格子数量。
示例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(i1)+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=x1x2 d y = y 1 − y 2 d_y=y_1-y_2 dy=y1y2

  • 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;
}
  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

_sky123_

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

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

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

打赏作者

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

抵扣说明:

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

余额充值