算法竞赛进阶指南打卡系列 0x00 11.分形之城

算法竞赛进阶指南打卡系列题解(摆烂好多天了,不能再摆了呜呜呜)


题目

原题链接

AcWing 98. 分形之城

题面

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

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

而这座名为 Fractal 的城市设想了这样的一个规划方案,如下图所示:
在这里插入图片描述
当城区规模扩大之后,Fractal 的解决方案是把和原来城区结构一样的区域按照图中的方式建设在城市周围,提升城市的等级。

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

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

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

输入格式
第一行输入正整数 n n n,表示测试数据的数目。

以下 n n n 行,输入 n n n 组测试数据,每组一行。

每组数据包括三个整数 N , A , B N,A,B N,A,B,表示城市等级以及两个街区的编号,整数之间用空格隔开。

输出格式
一共输出 n n n 行数据,每行对应一组测试数据的输出结果,结果四舍五入到整数。

数据范围
1 ≤ N ≤ 31 1≤N≤31 1N31,
1 ≤ A , B ≤ 2 2 N 1≤A,B≤2^{2N} 1A,B22N,
1 ≤ n ≤ 1000 1≤n≤1000 1n1000

输入样例:

3 
1 1 2 
2 16 1 
3 4 33 

输出样例:

10 
30 
50 

题解

思路

1 1 1 级城市如图所示,我们可以发现 2 2 2 级城市的左上角为 1 1 1 级城市顺时针旋转 90 ° 90° 90° 得到,左下角为逆时针旋转 90 ° 90° 90° 得到,右上角和右下角不变,同理可得 3 3 3 级城市和 2 2 2 级城市的关系, … , N − 1 N-1 N1 级城市与 N N N 级城市的关系。

因此我们只需要对给定的 N N N 级城市进行降维处理得到目标点在 1 1 1 级城市中的位置,然后在递归回来的过程中将其映射到 N N N 级城市即可。

假设城镇在 N − 1 N-1 N1 级城市的坐标为 ( x , y ) (x,y) (x,y)

  1. 若处于左上角的 N − 1 N-1 N1 级城市中,顺时针旋转 90 ° 90° 90° 变为 ( y , x ) (y,x) (y,x)
  2. 若处于右上角,则为 ( x , y + l e n ) (x,y+len) (x,y+len) l e n len len N − 1 N-1 N1 级城市的边长。
  3. 右下角, ( x + l e n , y + l e n ) (x+len,y+len) (x+len,y+len)
  4. 左下角, ( l e n ∗ 2 − 1 − y , l e n − 1 − x ) (len*2-1-y,len-1-x) (len21y,len1x)

细节见代码。

代码

#include <bits/stdc++.h>

// #define int long long

using namespace std;

constexpr int P = 998244353;
using i64 = long long;

struct Point
{
	i64 x, y;
};

Point get(i64 n, i64 a)
{
	if (n == 0) return {0, 0};
	// block为每个块的城市数量,len为当前等级城市的边长
	i64 block = 1ll << n * 2 - 2, len = 1ll << n - 1;
	auto p = get(n - 1, a % block);
	i64 x = p.x, y = p.y;
	int z = a / block;

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

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

	auto pa = get(n, a - 1); // 将编号映射到从0开始
	auto pb = get(n, b - 1);
	double dx = pa.x - pb.x, dy = pa.y - pb.y;
	printf("%.0lf\n", sqrt(dx * dx + dy * dy) * 10);
}

signed main()
{
	// ios::sync_with_stdio(false);
	// cin.tie(nullptr);

	int T;
	cin >> T;

	while (T -- ) {
		solve();
	}

	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值