希尔伯特曲线

2019牛客暑期多校训练营(第十场)

题解:实质上是给出点,转换为点的编号。然后对编号排序,从小到大输出点。

  • 对于等级n,边长为2^n,有2^2n个点。我们可以给点编号。这里有一个巧妙的方法,对于等级n,可以分治成n-1,以此类推。对于等级n,可以取2^(2n-2)为系数,就如代码中的v。比如等级3有64个点。那么等级3有系数16k3,k3∈[0,3]。等级2有4k2,k2∈[0,3],等级1有k1,k1∈[0,3]。我们可以用16k3 + 4k2 + k1表示64个点,范围为[0,63]。
  • 整体的顺序是左上角,左下角,右下角,右上角,k一次取0,1,2,3。
  • 左下角和右下角分治后顺序保持不变。左上角关于y = x对称,右上角关于y = -x + k / 2对称。这里对称之后注意还要加1,因为是网格对称,所以比较特殊,画个图就明白了。
#include <bits/stdc++.h>
using namespace std;
int const N = 1e6 + 10;
typedef long long ll;
int n,k;
struct Node
{
	int x,y;
	ll id;	
	bool operator < (const Node& e)const{
		return id < e.id;
	}
}node[N];
ll solve(int x,int y,ll k){
	if(k == 1)	return 0;
	ll v = k * k / 4;
	if(x <= k / 2){
		if(y <= k / 2)	return solve(y,x,k >> 1);  //左上角
		else	return solve(k - y + 1,k / 2 - x + 1,k >> 1) + 3 * v;
	}else{
		if(y <= k / 2)	return solve(x - k / 2,y,k >> 1) + v;
		else	return solve(x - k / 2,y - k / 2,k >> 1) + 2 * v;
	}
}
int main(){
	scanf("%d%d",&n,&k);
	for(int i=1;i<=n;i++){
		scanf("%d%d",&node[i].x,&node[i].y);
		node[i].id = solve(node[i].x,node[i].y,(1<<k));
	}
	sort(node+1,node+1+n);
	for(int i=1;i<=n;i++){
		printf("%d %d\n",node[i].x,node[i].y);
	}
	return 0;
}

分形之城

题解:求任意两点之间的直线距离。给出点的编号,可以转换为点的坐标。

  • 和上题类似,只不过是反着,点转换成坐标。
#include <bits/stdc++.h>
using namespace std;
template<class Ty1,class Ty2> 
inline const pair<Ty1,Ty2> operator + (const pair<Ty1, Ty2>&p1, const pair<Ty1, Ty2>&p2){
    pair<Ty1, Ty2> ret;
    ret.first = p1.first + p2.first;
    ret.second = p1.second + p2.second;
    return ret;
}
typedef long long ll;
typedef pair<ll,ll>pii;
pii p1,p2;
ll a,b,n;
pii solve(ll id,ll k){  
	if(k == 0)	return pii(0,0);
	ll len = 1ll<<k;
	ll v = 1ll<<(2 * k - 2);
	if(id < v){  //左上角,对角线翻转
		pii t = solve(id,k - 1);
		swap(t.first,t.second);
		return t;
	}else if(v <= id && id < 2 * v){  //右上角
		return solve(id - v,k - 1) + pii(0,len / 2);
	}else if(2 * v <= id && id < 3 * v){  //右下角
		return solve(id - 2 * v,k - 1) + pii(len / 2,len / 2);
	}else{  //左下角,次对角线翻转
		pii t = solve(id - 3 * v,k - 1);
		return pii(len - 1 - t.second,len / 2 - 1 - t.first);
	}
}
double getdis(pii p1,pii p2){
	ll dx = p1.first - p2.first;
	ll dy = p1.second - p2.second;
	return sqrt(dx * dx + dy * dy) * 10;
}
int main(){
	int T;
	scanf("%d",&T);
	while(T--){
		scanf("%lld%lld%lld",&n,&a,&b);
		p1 = solve(a - 1,n);
		p2 = solve(b - 1,n);
		printf("%.0f\n",getdis(p1,p2));
	}	
	return 0;
}
/*
对于每一个编号都有一个坐标,
 */

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值