分形之城(0x02 递推与递归)

分形之城

题意

城市的规划在城市建设中是个大问题。

不幸的是,很多城市在开始建设的时候并没有很好的规划,城市规模扩大之后规划不合理的问题就开始显现。

而这座名为 Fractal 的城市设想了这样的一个规划方案,如下图所示:

city.png

当城区规模扩大之后,Fractal 的解决方案是把和原来城区结构一样的区域按照图中的方式建设在城市周围,提升城市的等级。

对于任意等级的城市,我们把正方形街区从左上角开始按照道路标号。

虽然这个方案很烂,Fractal 规划部门的人员还是想知道,如果城市发展到了等级 N,编号为 A 和 B 的两个街区的直线距离是多少。

街区的距离指的是街区的中心点之间的距离,每个街区都是边长为 10 米的正方形。

思路

对于 n 级的城市,我们可以先假设当前编号的房屋在第 n - 1 级城市的位置(横纵坐标)已经知道(通过递归),那么只需要知道当前编号的房屋在哪个 n - 1级的城市中即可,因为 n - 1 级城市中有 2 2 n − 2 2^{2n-2} 22n2 个城市,令 z = m / 2 2 n − 2 z = m / 2^{2n-2} z=m/22n2 ,观察图中的编号可知,0表示左上的城市,1表示右上的城市,2表示右下的城市,3表示左下的城市,对不同的 n - 1级城市执行不同的坐标变换就可以得到目标房屋在当前 n 级城市中的横纵坐标。

假设当前城市等级为2 通过 z = = m / 2 2 n − 2 z == m / 2^{2n-2} z==m/22n2 求得 z

  1. z == 0 时,表示要求的房屋在第一个 1 级城市中,1 级城市经过顺时针旋转90°再水平翻转后得到 2 级城市左上角的部分

    (0,0) —> (0,0)

    (0,1) —> (1,0)

    (1,0) —> (0,1)

    (1,1) —> (1,1)

    所以坐标变换为 (x,y) —> (y,x)

  2. z == 1 时,表示要求的房屋在第二个 1 级城市中,1 级城市经过向右平移得到 2 级城市右上角的部分

    (0,0) —> (0,2)

    (0,1) —> (0,3)

    (1,0) —> (1,2)

    (1,1) —> (1,3)

    所以坐标变换为 (x,y) —> (x,y + len) len为 n - 1级城市的边长

  3. z ==2 时,表示要求的房屋在第三个 1 级城市中,1级城市经过向右再向下平移得到 2 级城市右下角的部分

    (0,0) —> (2,2)

    (0,1) —> (2,3)

    (1,0) —> (3,2)

    (1,1) —> (3,3)

    所以坐标变换表示为 (x,y) —> (x + len, y + len) len为 n - 1级城市的边长

  4. z == 3 时,表示要求的房屋在第四个 1 级城市中,1级城市经过逆时针旋转90°再向下平移得到 2 级城市的左下角的部分

    (0,0) —> (3,1)

    (0,1) —> (2,1)

    (1,0) —> (2,0)

    (1,1) —> (3,0)

    所以坐标 变换表示为 (x,y) —> (2 * len - y - 1, len - x - 1) len为 n - 1级城市的边长

为方便起见,将房屋从 0 开始编号,相应的,求a - 1 b - 1的位置然后计算距离,最后距离乘以 10 即可(因为每个街区都是 10米的正方形)

代码
#include<bits/stdc++.h>
#include<unordered_map>
// #define int long long
#define INF 0x3f3f3f3f
#define mod 1000000007
#define rep(i, st, ed) for (int (i) = (st); (i) <= (ed);++(i))
#define pre(i, ed, st) for (int (i) = (ed); (i) >= (st);--(i))

using namespace std;

typedef long long LL;
typedef pair<int, int> PII;
template<typename T> inline T gcd(T a, T b) { return b ? gcd(b, a % b) : a; }
template<typename T> inline T lowbit(T x) { return x & -x; }

const int N = 100;

pair<LL, LL>cal(LL n, LL m) {
	if (n == 0)return { 0,0 };

	LL cnt = 1ll << (2 * n - 2);
	LL len = 1ll << (n - 1);

	pair<LL, LL>t = cal(n - 1, m % cnt); // 求出 n - 1 级地图中的位置
	LL x = t.first, y = t.second;

	LL z = m / cnt; // 在上一级的哪个区域里

	if (z == 0)return { y,x };
	else if (z == 1)return { x,y + len };
	else if (z == 3)return { 2 * len - y - 1, len - x - 1 };
	else return { x + len,y + len };
}

void solve() {
	LL n, a, b; cin >> n >> a >> b;

	pair<LL, LL>p1 = cal(n, a - 1);
	pair<LL, LL>p2 = cal(n, b - 1);


	printf("%.0lf\n", sqrt((p1.first - p2.first) * (p1.first - p2.first) + (p1.second - p2.second) * (p1.second - p2.second)) * 10);
}

signed main() {
	int t; cin >> t;
	while (t--)
		solve();

	return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

zzqwtc

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

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

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

打赏作者

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

抵扣说明:

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

余额充值