题目链接:点击这里
这道题要求两个房子的直线距离,我们可以先求出两个房子的坐标,直接勾股定理来求。
首先要知道,一座 级城市有 个房子,我们先将问题缩小到 级城市, 级城市的总房子数为 ,我们可以用 来求得 级城市编号为 的房子 在 级城市中相应的编号,又可以根据 来确定 位于 级城市四块中的哪一块。
整体思路是递归+回溯。
例如,对一个
级城市编号为
的房屋来说,不断利用
得到该房屋在上一级城市中相应的编号,这样便可从
级城市递归到
级城市,即递归边界是
级城市、其坐标为
。
然后,不断利用
来确定房屋在当前级城市四块中的哪一块,再加上该块上正确的坐标变换,这样便可从
级城市回溯到
级城市,从而得到
级城市编号为
的房屋的坐标。
剩下最麻烦的就是回溯过程中的坐标变换了:
为了取模计算方便,城市中房屋的编号及其位置坐标我们都从 开始:
第0块:从 级城市到 级城市左上角那一块:先绕着原点顺时针旋转90度,即 -> ,然后水平翻转,即 -> ,最终 ->
第1块:从 级城市到 级城市右上角那一块:这个比较简单,就是向右平移了 ,即 ->
第2块:从 级城市到 级城市右下角那一块:也比较简单,就是向右平移了 ,然后向下平移了 ,即 ->
第3块:从 级城市到 级城市左下角那一块:这个较为麻烦,先绕着原点逆时针旋转90度,即 -> ,再水平翻转,即 -> ,然后向右平移 ,向下平移 ,即 -> ,最终 ->
对于上面的一系列坐标变换,能画图观察出来的最好,不能的话可以借助旋转矩阵来进行顺/逆时针旋转。
不管咋样,都要注意:本题的位置坐标指的是行号 和列号 ,与数学中的坐标轴有所区别。
#include<cmath>
#include<cstdio>
#include<string>
#include<utility>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
pair<ll,ll> calc(ll n, ll m)
{
if(n==0) return make_pair(0,0); //递归边界,0级城
ll len = 1ll<<(n-1), cnt = 1ll<<(2*n-2); //cnt是子问题里房屋的数量
pair<ll,ll> pos = calc(n - 1, m % cnt);
ll x = pos.first, y = pos.second;
ll z = m / cnt;
if(z==0) return make_pair(y, x);
if(z==1) return make_pair(x, y+len);
if(z==2) return make_pair(x+len, y+len);
if(z==3) return make_pair(2*len-y-1, len-x-1);
}
int main()
{
int t;
scanf("%d", &t);
while(t--)
{
ll N, A, B;
scanf("%lld%lld%lld", &N, &A, &B);
pair<ll,ll> apos = calc(N, A-1);
pair<ll,ll> bpos = calc(N, B-1);
double dx = apos.first - bpos.first, dy = apos.second - bpos.second;
printf("%.0f\n", sqrt(dx*dx+dy*dy)*10); //四舍五入到整数
}
return 0;
}