传送门
来自于题解:由题设 k 为正方形边长。首先我们可以通过确定正方形的底边来确定正方形。比如底边是由[x, y],[x + k, y]两点连成的边,那么这样的底边可以确定一个由 [x, y],[x + k, y],[x, y + k],[x + k, y + k]四个点组成的正方形。假设我 们用左下角的点[x, y]来代表这个正方形(下同)。 现在我们来考虑某个点(a,b)可以被怎样的正方形包含。那么我们 会发现,左下角的点的 x 坐标满足(a − k ≤ x ≤ a)且 y 坐标满足 (b − k ≤ y ≤ b)会包含(a,b)这个点。 我们不妨用扫描线的思想来解决这道题。我们从左到右地扫描 x 坐标,并用线段树维护当前 y 坐标上的点数量。即当目前扫描到区间 [a − k, a]时,点(a,b)对 y 坐标为[b-k,b]中的正方形有贡献。所以当 扫描到 a-k 时,将区间[b-k,b]全部+1。当扫描到 a 时,将区间对应 地-1。这样就可以描述(a,b)点对题目答案的贡献了。 具体的做法:每个点(a,b)存两遍,一遍存(a-k,b)且置 flag 为 1, 代表扫描到此点时要将区间+1,第二遍存(a,b)且置 flag 为-1,代表 扫描到此点时要将区间-1。然后按 x 坐标排序,用线段树扫描 x 坐标 即可。每次+1 之后区间查询全局最大值,更新答案。
//https://cometoj.com/problem/1016
#include<bits/stdc++.h>
using namespace std;
const int N = 2e5 + 10;
int n, k;
int x, y;
struct node{
int x, y, op;
bool operator < (const node& no) const {
if(x == no.x)
return op < no.op;
else return x < no.x;
}
};
vector<node> a;
int b[N];
int Max[N*4], lazy[N*4];
void push(int id){
if(lazy[id] != 0) {
lazy[id<<1] += lazy[id];
lazy[id<<1|1] += lazy[id];
Max[id<<1] += lazy[id];
Max[id<<1|1] += lazy[id];
lazy[id] = 0;
}
}
void up(int id, int l, int r, int ql, int qr, int v){
if(l >= ql && r <= qr){
lazy[id] += v;
Max[id] += v;
return ;
}
int mid = (l+r)/2;
push(id);
if(ql <= mid) up(id<<1, l, mid, ql, qr, v);
if(qr > mid) up(id<<1|1, mid+1, r, ql, qr, v);
Max[id] = max(Max[id<<1] , Max[id<<1|1]);
}
int main(){
cin >> n >> k;
for(int i = 1; i <= n; i++){
cin >> x >> y;
a.push_back(node{x - k, y, 1});
a.push_back(node{x, y, 2});
b[2*i-1] = y - k;
b[2*i] = y;
}
sort(b+1, b+1+2*n);
sort(a.begin(), a.end());
int len = unique(b+1, b+1+2*n) - b - 1;
int res = 0;
for(auto u:a){
int l = lower_bound(b+1, b+1+len, u.y - k) - b;
int r = lower_bound(b+1, b+1+len, u.y) - b;
int v;
if(u.op == 1)
v = 1;
else v = -1;
up(1, 1, len, l, r, v);
res = max(res, Max[1]);
}
cout << res << "\n";
return 0;
}