Comet oj C1016 [欢乐赛]茶颜悦色 线段树区间更新 + 扫描线

传送门

来自于题解:由题设 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;
}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值