51nod 3148 松鼠聚会(几何计算与计算)

题目描述:有N个小松鼠,它们的家用一个点x,y表示,两个点的距离定义为:点(x,y)和它周围的8个点即上下左右四个点和对角的四个点,距离为1。现在N个松鼠要走到一个松鼠家去,求走过的最短距离和。(0<=N<=10^5,
-10^9<=x, y<=10^9)。
我们可以看出此题可以用切比雪夫距离( dis=max(|x1−x2|,|y1−y2|) )来处理,因为:两个点的距离定义为:点(x,y)和它周围的8个点即上下左右四个点和对角的四个点,距离为1。这句话告诉我们两点之间的距离可以化为切比雪夫距离来处理在这里插入图片描述
然后你会发现如果直接这样处理的话会超时,那么我们可以将其转化成曼哈顿距离( dis=|x1−x2|+|y1−y2| )来处理。为什么呢?因为这两个东西可以互相转化.
假设(0,0)是原点,与它的曼哈顿距离是1的点构成的一个正方形
在这里插入图片描述
它的边长是√2,对角之间的距离是2。
与它切比雪夫距离为1点围成的图形也是一个正方形。
在这里插入图片描述
它的边长是2,对角之间的是2*√2。

我们可以发现,这两个东西是可以通过旋转来互相转化的,事实上,将(x,y)坐标变为(x+y,x−y)后,原坐标系曼哈顿距离=新坐标系切比雪夫距离。

另一种是将(x,y)坐标变成((x+y)/2,(x-y)/2),那么原坐标切比雪夫距离=新坐标的曼哈顿距离。

通过这种的转化,将切比雪夫距离转化成曼哈顿距离来求和,可以用排序来去掉绝对值的影响,再用前缀和优化,可以把复杂度降为O(1)。

那么这道题就可以通过这种方式来解决,先求出将所有点转化成((x+y)/2,(x-y)/2),再对他们进行排序,求前缀和,求出新曼哈顿距离即原切比雪夫距离就ok了。

#include <cstdio>
#include <cstring>
#include <algorithm>
#define N 100010
using namespace std;
typedef long long ll;
ll x[N] , y[N] , tx[N] , ty[N] , sx[N] , sy[N];
int main()
{
	int n , i , px , py;
	ll ans = 1ll << 62 , vx , vy;
	scanf("%d" , &n);
	for(i = 1 ; i <= n ; i ++ ) scanf("%lld%lld" , &vx , &vy) , x[i] = tx[i] = vx + vy , y[i] = ty[i] = vx - vy;
	sort(tx + 1 , tx + n + 1) , sort(ty + 1 , ty + n + 1);
	for(i = 1 ; i <= n ; i ++ ) sx[i] = sx[i - 1] + tx[i] , sy[i] = sy[i - 1] + ty[i];
	for(i = 1 ; i <= n ; i ++ )
	{
		px = lower_bound(tx + 1 , tx + n + 1 , x[i]) - tx , vx = (x[i] * px - sx[px]) + (sx[n] - sx[px] - x[i] * (n - px));
		py = lower_bound(ty + 1 , ty + n + 1 , y[i]) - ty , vy = (y[i] * py - sy[py]) + (sy[n] - sy[py] - y[i] * (n - py));
		ans = min(ans , vx + vy);
	}
	printf("%lld\n" , ans >> 1);
	return 0;
}
 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值