题意:在坐标轴上给你n个点(n<=1e5),点的横纵坐标是[0, 1e5],你可以从选3行,每行间隔是d,选3列同样间隔d,点只能被选一次,问你最多能选多少点。d<=1e5
思路:我们考虑对列建一棵线段树,每个结点放的是num[j] + num[j + d] + num[j + 2d] 的值,然后我们枚举行的同时删除选中的点,然后线段树查询最大值就行了。删除点的时候只会对自己x,x-d,x-2d的位置产生影响,删除过查询后要重新加上。
#include
using namespace std;
typedef long long ll;
#define ls rt << 1
#define rs rt << 1|1
#define lson l, mid, rt << 1
#define rson mid + 1, r, rt << 1|1
const int N = 100005;
const int maxn = 2e5 + 10;
const ll inf = 0x3f3f3f3f3f3f3f3f;
vector v[maxn];
int num[maxn]; //记录每列的数量
ll tree[maxn << 2];
int n, d;
void push_up(int rt)
{
tree[rt] = max(tree[ls], tree[rs]);
}
void build(int l, int r, int rt)
{
if(l == r)
{
for(int i = 0; i < 3; ++i)
if(l + i * d <= N)
tree[rt] += num[l + i * d];
return;
}
int mid = (l + r) >> 1;
build(lson);
build(rson);
push_up(rt);
}
void update(int pos, int val, int l, int r, int rt)
{
if(l < 0 || r > N || pos < 0 || pos > N)
return;
if(l == r)
{
tree[rt] += val;
return;
}
int mid = (l + r) >> 1;
if(pos <= mid)
update(pos, val, lson);
else
update(pos, val, rson);
push_up(rt);
}
ll query(int ql, int qr, int l, int r, int rt)
{
if(ql <= l && r <= qr)
return tree[rt];
int mid = (l + r) >> 1;
ll ret = -inf;
if(ql <= mid)
ret = max(ret, query(ql, qr, lson));
if(qr > mid)
ret = max(ret, query(ql, qr, rson));
return ret;
}
int main()
{
scanf("%d%d", &n, &d);
for(int i = 1; i <= n; ++i)
{
int x, y;
scanf("%d%d", &x, &y);
v[y].push_back(x);
num[x]++;
}
build(0, N, 1);
ll ans = 1;
for(int i = 0; i <= N; ++i) //枚举行
{
ll tmp = 0;
for(int j = 0; j < 3; ++j)
{
if((ll)(i + j * d) <= N)
{
tmp += v[i + j * d].size();
for(auto x : v[i + j * d])
{
update(x, -1, 0, N, 1);
update(x - d, -1, 0, N, 1);
update(x - 2 * d, -1, 0, N, 1);
}
}
}
tmp += query(0, N, 0, N, 1);
ans = max(ans, tmp);
for(int j = 0; j < 3; ++j)
{
if((ll)(i + j * d) <= N)
{
for(auto x : v[i + j * d])
{
update(x, 1, 0, N, 1);
update(x - d, 1, 0, N, 1);
update(x - 2 * d, 1, 0, N, 1);
}
}
}
}
printf("%lld\n", ans);
return 0;
}