一、内容
城市的规划在城市建设中是个大问题。
不幸的是,很多城市在开始建设的时候并没有很好的规划,城市规模扩大之后规划不合理的问题就开始显现。
而这座名为 Fractal 的城市设想了这样的一个规划方案,如下图所示:
当城区规模扩大之后,Fractal 的解决方案是把和原来城区结构一样的区域按照图中的方式建设在城市周围,提升城市的等级。
对于任意等级的城市,我们把正方形街区从左上角开始按照道路标号。
虽然这个方案很烂,Fractal 规划部门的人员还是想知道,如果城市发展到了等级 N,编号为 A 和 B 的两个街区的直线距离是多少。
街区的距离指的是街区的中心点之间的距离,每个街区都是边长为 10 米的正方形。
输入格式
第一行输入正整数n,表示测试数据的数目。以下n行,输入n组测试数据,每组一行。每组数据包括三个整数 N,A,B, 表示城市等级以及两个街区的编号,整数之间用空格隔开。
输出格式
一共输出n行数据,每行对应一组测试数据的输出结果,结果四舍五入到整数。
数据范围
1≤N≤31
1≤A,B≤22N,
1≤n≤1000
输入样例:
3
1 1 2
2 16 1
3 4 33
输出样例:
10
30
50
二、思路
- 坐标翻转公式:θ为翻转的角度,通过这个公式就可以计算出翻转90°后坐标改变。
- 本题关键是求编号为M(我们从0开始编号)在N级城市中的坐标,我们记为calc(N, M). 本题求解的就是calc(N, A), calc(N, B)2个坐标的距离。
- 由于N级城市是由4座N-1城市构成的,所以在求解cal(N, M)的时,因为N-1城市的房子数量为22N,所以我们先递归求calc(N - 1, M mod 22N-2),记求出的坐标为(x, y).
- 根据求出来的(x, y) 很容易就能推出该房子在N级城市的坐标。
1.若编号M处于左上的N-1级城市中,则最终坐标为(y, x) 因为是顺时针翻转90° 再水平翻转一下。(因为头和尾的位置不同,所以要水平翻转一下。)
2.若处于右上的N-1级城市中,直接就是(x , y + 2 N-1) 2N-1就是N-1城市的边长
3.若处于右下的N-1级城市中,最终坐标为(x + 2N-1, y + 2N-1)。
4.左下: (2N - y - 1, 2N-1 - x - 1)。
三、代码
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <iostream>
typedef long long ll;
using namespace std;
pair<ll, ll> calc(ll n, ll a) {
if (n == 0) return make_pair(0, 0);
//注意转化为long long
ll len = 1LL << (n - 1); //每个小方格的长度
ll cnt = 1LL << (2 * n - 2);//每个小正方形有多少个房子
pair<ll, ll> tem = calc(n - 1, a % cnt);
ll x = tem.first; ll y = tem.second;
ll m = a / cnt; //表示在那个小正方形中
if (m == 0) return make_pair(y, x);
if (m == 1) return make_pair(x, y + len);
if (m == 2) return make_pair(x +len, y + len);
if (m == 3) return make_pair(2 * len - y - 1, len - x - 1);
}
int main() {
ll t, n, a, b;
scanf("%lld", &t);
while (t--) {
scanf("%lld%lld%lld", &n, &a, &b);
//编号从0开始
pair<ll, ll> p1 = calc(n, a - 1);
pair<ll, ll> p2 = calc(n, b - 1);
//计算2点距离
ll x1 = p1.first; ll y1 = p1.second;
ll x2 = p2.first; ll y2 = p2.second;
ll tem1 = x1 - x2;
ll tem2 = y1 - y2;
double ans = sqrt((tem1 * tem1 + tem2 * tem2)) * 10; //最后*10避免超出long long
printf("%.0f\n", ans);
}
return 0;
}