松鼠聚会
松鼠聚会
题意:网格图,有\(n\)个小松鼠,每个小松鼠到周围八个点的距离为\(1\),选定一个小松鼠的位置,是的所有松鼠到这个位置的最小路程和。
数据范围:
\(0 \le n \le 1e5 \,\, -10^9 \le x\le 10^9\)
解法:
题意中两个松鼠的最大距离即为切比雪夫距离:两个点横纵坐标之差的较大值
曼哈顿距离:两个点横纵坐标的差的绝对值之和
\(OI\)常用套路:
将一个点的坐标\((x,y)\)变为\((x + y, x - y)\) 后,原坐标系中的曼哈顿距离 = 新坐标系中的切比雪夫距离
反过来,将一个点的坐标\((x,y)\)变为\((\frac{x+y}{2},\frac{x-y}{2})\)后,原坐标系中的切比雪夫距离 = 新坐标系中的曼哈顿距离
这样我们就可以将原问题进行转化
不妨将转化后的\(x,y\)坐标进行排序,对一个点\(k\)答案就是统计\(x\)坐标:\(x[k]\times t-\sum_{i=1}^tx[i](x[t] < x[k]) +\sum_{i=k+1}^{n}x[i]- x[k]\times t\),统计\(y\)坐标同理
所以我们可以采用前缀和优化:
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
#define maxn 100100
#define ll long long
ll x1[maxn], y1[maxn];
ll x2[maxn], y2[maxn];
ll sumx[maxn], sumy[maxn];
int n;
int query1(ll x)
{
int l = 1, r = n, ans = 0;
while(l <= r)
{
int mid = (l + r) >> 1;
if(x2[mid] <= x)
{
ans = mid;
l = mid + 1;
}
else r = mid - 1;
}
return ans;
}
int query2(ll y)
{
int l = 1, r = n, ans = 0;
while(l <= r)
{
int mid = (l + r) >> 1;
if(y2[mid] <= y)
{
ans = mid;
l = mid + 1;
}
else r = mid - 1;
}
return ans;
}
int main()
{
scanf("%d", &n);
for(int i = 1; i <= n; i++)
{
int x, y;
scanf("%d%d", &x, &y);
x1[i] = x2[i] = x + y;
y1[i] = y2[i] = x - y;
}
sort(x2 + 1, x2 + n + 1);
sort(y2 + 1, y2 + n + 1);
for(int i = 1; i <= n; i++)
{
sumx[i] = sumx[i - 1] + x2[i];
sumy[i] = sumy[i - 1] + y2[i];
}
ll ans = 1e15;
for(int i = 1; i <= n; i++)
{
//printf("x1[%d] = %d y1[%d] = %d\n", i, x1[i], i, y1[i]);
ll sum = 0;
int x = query1(x1[i]), y = query2(y1[i]);
sum += x * x2[x] - sumx[x] + sumx[n] - sumx[x - 1] - (n - x + 1) * x2[x];
sum += y * y2[y] - sumy[y] + sumy[n] - sumy[y - 1] - (n - y + 1) * y2[y];
ans = min(ans, sum);
}
printf("%lld\n", ans >> 1);
return 0;
}