分形之城
有一座城市如图所示,
等级为1的城市已经给出,第i-1级城市升高到第i级城市的做法是将i-1级城市复制一份向下平移到与原城市上端与之对齐,然后将其复制一份向右平移到与原城市左端对其,在沿其中心顺时针旋转90度,最后再复制一份,逆时针沿中心旋转90度,然后平移到已经处理好的三座城市的左下角,最后用道路把城市首尾连接起来,然后从左上角第一座房子沿道路出发依次给房子编号\(1\sim n\),可以参考上图,现在询问n级城市的房屋a和房屋b的直线距离,\(n\leq 31\)。
[注:本人语文不行,题目可能没说清楚,最好参考原题]
解
以下为了代码实现的方便,将所有城市的编号全部-1
显然是一个无限分形图,于是首要考虑递归法,不妨定义\(calc(n,m)\)为房屋m在n级城市中的坐标,我们把坐标原点定在城市中心,x轴水平方向,y轴竖直方向,通过找规律容易知道第i级城市的房屋数\(2^{2n}\)个,因为n级城市是由4个n-1级相同城市组成,只是发生的旋转,可以把子问题看作n-1级城市,容易知道房屋m在n-1级城市中编号应该为\(n\% 2^{2n-2}\)
现在关键在于如何通过\(calc(n-1.m\% 2^{2n-2})\)求出\(calc(n,m)\)
现在需要分类讨论,m在n级城市中的那一块,不妨左上角记为0,右上角记为1,右下角记为2,左下角记做3,记\(calc(n-1,m\% 2^{2n-2})\)为\((x,y)\),于找规律结合平面直角坐标系的知识有(其中找规律有n级城市的边长为\(2^n\))
\(calc(n,m)=\begin{cases}(-y-2^{n-2},-x+2^{n-2})(m\% 2^{2n-2}==0)\\(x+2^{n-2},y+2^{n-2})(m\% 2^{2n-2}==1)\\(x+2^{n-2},y-2^{n-2})(m\% 2^{2n-2}==2)\\(y-2^{n-2},x-2^{n-2})(m\% 2^{2n-2}==3)\end{cases}\)
以此依据递归处理即可,时间复杂度\(O(n)\)
参考代码:
#include <iostream>
#include <cstdio>
#include <cmath>
#define il inline
#define ri register
#define ll long long
#define lb long double
using namespace std;
struct pos{
ll x,y;
}beg[4];
ll base[63];
il lb dis(pos,pos);
il pos divide(int,ll);
int main(){
beg[0]=(pos){-5,5},beg[1]=(pos){5,5};
beg[2]=(pos){5,-5},beg[3]=(pos){-5,-5};
base[0]=1;int lsy,n;ll a,b;scanf("%d",&lsy);
for(int i(1);i<63;++i)base[i]=base[i-1]<<1;
while(lsy--)
scanf("%d%lld%lld",&n,&a,&b),--a,--b,
printf("%.0Lf\n",dis(divide(n,a),divide(n,b)));
return 0;
}
il lb dis(pos a,pos b){
return sqrt(pow(a.y-b.y,2)+pow(a.x-b.x,2));
}
il pos divide(int n,ll m){
if(n==1)return beg[m];
pos a(divide(n-1,m%base[2*n-2]));switch(m/base[2*n-2]){
case 0:return (pos){-a.y-base[n-2]*10,-a.x+base[n-2]*10};break;
case 1:return (pos){a.x+base[n-2]*10,a.y+base[n-2]*10};break;
case 2:return (pos){a.x+base[n-2]*10,a.y-base[n-2]*10};break;
case 3:return (pos){a.y-base[n-2]*10,a.x-base[n-2]*10};break;
}
}