分形之城

题目描述

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

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

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

在这里插入图片描述

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

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

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

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

输入格式

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

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

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

输出格式

一共输出 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 和等级2 两张图之间的关系。

等级2 图可以分为左上、右上、左下、右下四个部分,而左上可以通过等级1 图绕 1 点顺时针旋转 9 0 o 90^o 90o ,然后水平翻转得到,右上和右下和等级 1 图相同,左下可以通过等级 1 图绕 1 点顺时针旋转 27 0 o 270^o 270o ,然后竖直翻转得到 (图片可能不是很清楚,注意街区下标顺序)。

同理等级2 和等级3 两张图的关系亦是如此。

所以很明显可以使用递归进行处理。

如果我们需要求取等级3 中某一点的坐标,我们可以先将等级3 图划分为四个部分,然后计算出该点对应到等级2 图中的坐标,然后通过两者之间的转换关系,即可得到在等级3 中该点的坐标。为求解等级2 图中的坐标,以同样的方式计算等级 1中的坐标即可得到。

到此,可以看出本题目的难点在于坐标旋转 (我们假定坐标轴 x 向下,坐标轴 y 向右)。

假定一个坐标为 ( x , y ) (x,y) (x,y) ,现需要将其旋转 θ \theta θ 角度,那么旋转后的坐标为 :

( x 1 , y 1 ) = ( x , y ) [ c o s θ s i n θ − s i n θ c o s θ ] = ( x × c o s θ − y × s i n θ , x × s i n θ + y × c o s θ ) (x_1,y_1) = (x,y) \left[ \begin{matrix} cos\theta & sin\theta \\ -sin\theta & cos\theta \end{matrix} \right] = (x \times cos\theta - y \times sin\theta,x \times sin\theta + y \times cos\theta) (x1,y1)=(x,y)[cosθsinθsinθcosθ]=(x×cosθy×sinθ,x×sinθ+y×cosθ)

那么我们很容易得到,

如果旋转 9 0 o 90^o 90o ,旋转后坐标为 ( − y , x ) (-y,x) (y,x)

如果旋转 18 0 o 180^o 180o ,旋转后坐标为 ( − x , − y ) (-x,-y) (x,y)

如果旋转 27 0 o 270^o 270o ,旋转后坐标为 ( y , − x ) (y,-x) (y,x)

#include <iostream>
#include <cmath>
#include <iomanip>
using namespace std;

typedef long long LL;
typedef pair<LL,LL> pLL;

pLL cal(LL n,LL m)
{
    if(n == 0)
    {
        return {0,0};
    }
    
    // 当前等级图划分为四部分,每部分的长度len 和每部分包含的街区数
    LL len = 1ll << (n - 1),cnt = 1ll << (2 * n - 2);
    // 计算该位置在上一等级图中的坐标
    pLL position = cal(n - 1,m % cnt);
    LL x = position.first,y = position.second;
    LL z = m / cnt; // 判断是在哪一个部分,根据不同部分进行坐标转换
    if(z == 0)
    {
        return {y,x};
    }
    else if(z == 1)
    {
        return {x,y + len};
    }
    else if(z == 2)
    {
        return {x + len,y + len};
    }
    else if(z == 3)
    {
        // 这里的坐标转换最为复杂,建议画图查看
        return {2 * len - 1 - y,len - 1 - x};
    }
}


int main()
{
    int T;
    cin >> T;
    while(T--)
    {
        LL N,A,B;
        cin >> N >> A >> B;
        pLL pa = cal(N,A-1);
        pLL pb = cal(N,B-1);
        double x = pa.first - pb.first;
        double y = pa.second - pb.second;
        cout << fixed << setprecision(0) << sqrt(x * x + y * y) * 10 << endl;
    }
    
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值