切比雪夫距离小记
要不是做 JOI 我还不知道有这个东西。
定义
我们知道曼哈顿距离。假如点 \(a\) 坐标为 \((x1, y1)\),点 \(b\) 坐标为 \(x2, y2\),那么他们的曼哈顿距离为:\(|x1 - x2|+|y1-y2|\)。
现在我们看到切比雪夫距离的定义。如果你会玩国际象棋,你可以把点 \(a\) 与点 \(b\) 切比雪夫距离理解为国际象棋中的王从 \(a\) 走到 \(b\) 的最小步数。王每一步可以走一格,同时不仅能走上下左右,还能走斜线。
假如点 \(a\) 坐标为 \((x1, y1)\),点 \(b\) 坐标为 \(x2, y2\),那么他们的切比雪夫距离为:\(max(|x1 - x2|,|y1-y2|)\)。
证明:如果王从 \((x1,y1)\) 出发,没有到 \(x2\) 这一列,同时没有到 \(y2\) 这一行,那么它就能走斜线,从而将路程减少 \(1\) 。这样最多能走到横或竖到达终点,剩下的只能走直线。于是不难得出上式。
平心而论我觉得这个定义十分的扯淡,为什么会有人用国际象棋里棋子的走法定义距离呢。。。但国际象棋确实好玩
转化
这个东西和曼哈顿距离肯定有说不清道不明的关系!
我们令 \(d(a, b)\) 为 \(a\) 与 \(b\) 的曼哈顿距离,\(c(a, b)\) 为 \(a\) 与 \(b\) 的切比雪夫距离。
那么有:\(d(a, b) = |x1 - x2|+|y1-y2| = max(x1-x2+y1-y2,x2-x1+y1-y2,x1-x2+y2-y1,x2-x1+y2-y1)\)。实际上是把绝对值的所有情况列出,最大的一项为两个非负数相加,即原绝对值之和。
所以 \(d(a,b)=max(|(x1+y1)-(x2+y2)|, |(x1-y1)-(x2-y2)|)\) 发现这个东西就是切比雪夫距离的定义。然后我们得出了曼哈顿距离与切比雪夫距离的关系:
将原坐标系的每一个点 \((x,y)\) 转为 \((x+y, x-y)\) 此时新坐标系中的切比雪夫距离等于原坐标系的曼哈顿距离。
你想把他反过来也是可以的:
将原坐标系的每一个点 \((x,y)\) 转为 \((\frac{x+y}{2}, \frac{x-y}{2})\) 此时新坐标系中的曼哈顿距离等于原坐标系的切比雪夫距离。
应用
切比雪夫距离相比与曼哈顿距离多了个 max ,对多个切比雪夫距离求和时很难受。如果把切比雪夫距离转成曼哈顿距离就有可能能用排序优化掉绝对值进行求解了。
有这种直接转移的憨批题:[TJOI2013]松鼠聚会
直接把切比雪夫距离转为曼哈顿距离后前缀和求解就行。
那有人就要说了:那我学这个东西有个 * 用?
目前为止好像确实是这样,但你先别急。
看看这篇文章的写作起点: [JOISC 2021 Day2] 道路の建設案
尽管切比雪夫距离求和很无力,但是因为 max 的存在,比大小却是一等一的。这道题就是个很好的例子。
我们二分第 \(k\) 小的距离,判断比他小的有多少个。
把曼哈顿距离转为切比雪夫距离后,判断大小就变成了二维数点问题了。这道题用 set 一个一个跳,每次二分最多跳 \(k\) 次,所以复杂度为 \(O(n \log k)\)。
#include <bits/stdc++.h>
#define ll long long
#define inf 2147483647
#define forp(i, a, b) for(ll i = (a);i <= (b);i ++)
using namespace std;
bool chkmin(ll &a, ll b) {return (b < a ? a = b, true : false);}
bool chkmax(ll &a, ll b) {return (b > a ? a = b, true : false);}
const ll maxn = 3e5 + 5;
ll n, k;
struct point{
ll x, y;ll id;
bool operator <(const point &p) const{
return p.y > y || (p.y == y && p.x > x);
}
}city[maxn];
void change(point &p){
int x = p.x, y = p.y;
p = point{x + y, x - y, 0};
}
ll ans[maxn];
ll cnt = 0;
bool cmp(point a, point b){ return a.x < b.x; }
bool check(ll dis){
cnt = 0;
set<point> s;queue<point> q;
forp(i, 1, n){
while(!q.empty() && city[i].x - q.front().x > dis) s.erase(q.front()), q.pop();
set<point>::iterator it = s.lower_bound(point{-inf, city[i].y - dis, 0});
while(it != s.end() && abs((*it).y - city[i].y) <= dis){
ans[++ cnt] = max(abs((*it).x - city[i].x), abs((*it).y - city[i].y));
if(cnt == k) return true;
it ++;
}
q.push(city[i]);
s.insert(city[i]);
}
return false;
}
signed main()
{
freopen("text.in", "r", stdin);
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
cin >> n >> k;
set<point> s;
forp(i, 1, n) cin >> city[i].x >> city[i].y;
forp(i, 1, n) change(city[i]);
forp(i, 1, n) s.insert(city[i]);
set<point>::iterator it = s.begin();
sort(city + 1, city + n + 1, cmp);
ll l = 1, r = 4e9 + 1;
ll fin = 0;
while(l <= r){
ll mid = l + r >> 1;
if(check(mid)) fin = mid, r = mid - 1;
else l = mid + 1;
}
check(fin - 1);
sort(ans + 1, ans + cnt + 1);
forp(i, 1, cnt) cout << ans[i] << endl;
forp(i, cnt + 1, k) cout << fin << endl;
return 0;
}