考虑将曼哈顿距离转换成切比雪夫距离,这样问题就变成了 m a x ( ∣ x 1 − x 2 ∣ , ∣ y 1 − y 2 ∣ ) ≤ d max(|x_1-x_2|,|y_1-y_2|)\le d max(∣x1−x2∣,∣y1−y2∣)≤d,这个式子就很好看了,我们首先按照 ( x , y ) (x,y) (x,y)排序,让后我们双指针控制 ∣ x r − x l ∣ < = d |x_r-x_l|<=d ∣xr−xl∣<=d,用 m u l t i s e t multiset multiset维护 [ l , r ] [l,r] [l,r]内的 y y y坐标,一开始比较暴力的想法就是找到 [ y r − d , y r + d ] [y_r-d,y_r+d] [yr−d,yr+d]区间内的数,让后用并查集维护,但是这样显然是可以卡成 O ( n 2 ) O(n^2) O(n2)的,仔细想一下,有必要遍历所有点吗?并没有,假设我们二分找到了 ≥ y r − d \ge y_r-d ≥yr−d的第一个位置 p o s pos pos,那么不难发现 [ p o s , p o s + d ] [pos,pos+d] [pos,pos+d]对应的数都已经合并到一个集合中了,并没有必要去遍历,所以我们只需要找 y r − d , y r + 1 y_r-d,y_r+1 yr−d,yr+1对应在 m u l t i s e t multiset multiset里面的位置即可。
复杂度 O ( n l o g n ) O(nlogn) O(nlogn)
#include<bits/stdc++.h>
#define X first
#define Y second
#define L (u<<1)
#define R (u<<1|1)
#define Mid (tr[u].l+tr[u].r>>1)
#define pb push_back
using namespace std;
const int N=1000010,INF=0x3f3f3f3f,mod=1e9+7;
typedef long long LL;
typedef pair<int,int> PII;
int n,d;
int p[N],se[N];
struct Point {
int x,y;
bool operator < (const Point &W) const {
if(x!=W.x) return x<W.x;
else return y<W.y;
}
}a[N];
int find(int x) {
return x==p[x]? x:p[x]=find(p[x]);
}
void solve() {
multiset<PII>s;
scanf("%d%d",&n,&d);
for(int i=1;i<=n;i++) {
int x,y;
scanf("%d%d",&x,&y);
p[i]=i;
a[i].x=x+y; a[i].y=x-y;
}
sort(a+1,a+1+n);
for(int l=1,r=1;r<=n;r++) {
s.insert({a[r].y,r});
while(a[r].x-a[l].x>d) {
s.erase(s.find({a[l].y,l}));
l++;
}
int y=a[r].y;
auto it=s.lower_bound({y-d,0});
if(it!=s.end()) {
PII u=*it;
if(u.X<=y+d) {
p[find(r)]=find(u.Y);
it=s.lower_bound({y+1,0});
if(it!=s.end()) {
u=*it;
if(u.X<=y+d) {
p[find(r)]=find(u.Y);
}
}
}
}
}
int ans,mx;
ans=0; mx=0;
for(int i=1;i<=n;i++) {
if(se[find(i)]==0) ans++;
se[find(i)]++;
mx=max(mx,se[find(i)]);
}
printf("%d %d\n",ans,mx);
}
int main() {
int _=1;
while(_--) {
solve();
}
return 0;
}
/*
*/