【ACWing】98. 分形之城

题目地址:

https://www.acwing.com/problem/content/100/

城市的规划在城市建设中是个大问题。不幸的是,很多城市在开始建设的时候并没有很好的规划,城市规模扩大之后规划不合理的问题就开始显现。而这座名为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

以最左上角的房子为坐标原点,竖直向下是 x x x轴,水平向右是 y y y轴,建立坐标系(按照二维数组的习惯建立坐标系)。考虑如何递归求解。设 f ( n , s ) f(n, s) f(n,s) s s s号房屋在第 n n n个等级里的坐标。首先当 N = 0 N=0 N=0的时候,房子的坐标是 ( 0 , 0 ) (0,0) (0,0),所以 f ( 0 , 0 ) = ( 0 , 0 ) f(0,0)=(0,0) f(0,0)=(0,0)(为了方便我们从 0 0 0开始对房屋计数)。当 N = n N=n N=n的时候,一共有 2 2 n 2^{2n} 22n个房屋,分成左上、右上、左下和右下一共 4 4 4个部分,每个部分有 2 2 n − 2 2^{2n-2} 22n2个房屋。我们可以先将 s s s 2 2 n − 2 2^{2n-2} 22n2取余,这样就得到了它在第 n − 1 n-1 n1等级中的坐标,再求一下 ⌊ s 2 2 n − 2 ⌋ \lfloor\frac{s}{2^{2n-2}}\rfloor 22n2s,这就求出了它在左上、右上、左下还是右下, 0 0 0对应左上, 1 1 1对应右上, 2 2 2对应左下, 3 3 3对应右下。如果在右上,则只需要将 f ( n − 1 , s m o d    2 2 n − 2 ) f(n-1,s\mod 2^{2n-2}) f(n1,smod22n2)向右平移 2 n − 1 2^{n-1} 2n1即可,即 y y y增加 2 n − 1 2^{n-1} 2n1;如果在右下,则 x x x y y y同时增加 2 n − 1 2^{n-1} 2n1;如果在左上,则 x x x y y y交换位置;如果在左下,则做变换 ( x , y ) → ( 2 n − 1 − y , 2 n − 1 − 1 − x ) (x,y)\to (2^n-1-y,2^{n-1}-1-x) (x,y)(2n1y,2n11x),其实就是求 ( x , y ) (x,y) (x,y)关于 x = − y + 2 n − 1 x=-y+2^n-1 x=y+2n1的对称点,设这个对称点是 ( x ′ , y ′ ) (x',y') (x,y),那么应该有 x + x ′ = − ( y + y ′ ) + 2 n + 1 − 2 x+x'=-(y+y')+2^{n+1}-2 x+x=(y+y)+2n+12,还有斜率关系 x − x ′ = y − y ′ x-x'=y-y' xx=yy解之即可。这样就可以递归求解了。代码如下:

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

struct Point {
    long x, y;
};

Point get(long n, long a) {
    if (n == 0) return {0, 0};
    long block = 1l << 2 * n - 2, len = 1l << n - 1;
    auto p = get(n - 1, a % block);
    long x = p.x, y = p.y;

    int z = a / block;
    if (!z) return {y, x};
    else if (z == 1) return {x, y + len};
    else if (z == 2) return {x + len, y + len};
    else return {len * 2 - 1 - y, len - 1 - x};
}

int main() {
    int T;
    scanf("%d", &T);
    while (T--) {
        long n, a, b;
        scanf("%ld%ld%ld", &n, &a, &b);
        auto pa = get(n, a - 1), 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);
    }

    return 0;
}

时空复杂度 O ( N ) O(N) O(N)

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值