每日一题——Fractal Streets

题目

Fractal Streets

题解

这题的难点在坐标变换和如何进行递归如何化成子问题,我们可以将n级图变成4个n-1级图,而每个n-1级图通过找规律可以知道有4^(n-1)个格子记为cnt,这样我们就可以知道编号m的位置了,m在第m/cnt个n-1级图中,他的编号为m%cnt,这样子我们的递归就能写出来了,退出条件是n为0时,这个时候坐标点为(0,0),因为我们以上的操作,所以编号要从0开始,所以题目所给的AB要进行-1操作。接下来就是看落到第几块中进行怎样的坐标变换了,这里有个知识:

旋转矩阵(逆时针)
cosθ -sinθ
sinθ cosθ

以下操作的旋转点在编号为0的点(之前就是卡在这里题解理解不了)。
对于块0只需要顺时针旋转90°再进行水平翻转。 (x,y) -> (-y,x) -> (y,x)
对于块1和块2只需要平移即可。
对于块3操作比较复杂,首先要逆时针旋转90°,然后水平翻转,最后再进行平移。 (x,y) -> (y,-x) -> (-y,-x) -> (2*len-1-y,len-1-x)

参考代码

#include<iostream>
#include<cmath>
using namespace std;
typedef long long LL;
typedef pair<LL, LL> PLL;
PLL calc(LL n, LL m){
    if(!n) return {0,0};
    LL len = 1ll << (n - 1), cnt = 1ll << 2 * (n - 1);
    auto pos = calc(n - 1, m % cnt);
    auto x = pos.first, y = pos.second;
    auto z = m / cnt;
    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};
}
int main(){
    int T;
    cin >> T;
    LL N, A, B;
    while(T --){
        cin >> N >> A >> B;
        auto ac = calc(N, A - 1);
        auto bc = calc(N, B - 1);
        double x = ac.first - bc.first, y = ac.second - bc.second;
        printf("%.0lf\n",sqrt(x * x + y * y) * 10);
    }
    return 0;
}
posted @ 2025-03-08 22:03  PZnwbh  阅读(59)  评论(0)    收藏  举报