题目描述:有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;
}