线段树+扫描线+离散化

题意

T组数据,每次给你n个点,含点权

给你一个矩形,只能底边平行x轴放置

求矩形内点权和的最大值

T≤10,n≤1e5

分析

首先,看到n和xy的范围,想到离散化
其次, 我们求的是一个框框(范围)内的东西, 特别是有扫描线,且能够出入,所以想到线段树
(有点牵强...主要是因为我们做的就是线段树题目...
想想怎么实现,怎么做题
因为是一个框框,它框住某点的位置有多个,这样不好维护线段树,所以
我们把每个点(i,j)作为左下角,向右向上延生至(i+w-1,j+h-1),这个矩形加上(i,j)的贡献(即亮度 )(注:不包括边框)
不考虑横坐标,它能影响到的矩形为:y到y+h的点所代表的所有矩形。
考虑上横坐标,用扫描线,及时删除不在当前横坐标能覆盖的星星。

参考代码&分析

https://www.luogu.org/blog/HotoChino/solution-p1502

错误代码(留坑代填(wtm做了一下午....还是没有找到错误

#include<cstdio>
#include<string.h>
#include<algorithm>
using namespace std;
#define MAX 10000+9

int n,w,h;
int mx[MAX<<3], day[MAX<<1], addv[MAX<<2];//Discrete Array y 离散化y数组 
//线段树四倍,扫描线两倍 
struct seg{//平行于y轴的扫描线 
	int x, y1, y2, l;
	seg(int x=0, int y1=0, int y2=0, int l=0) : x(x), y1(y1), y2(y2), l(l) {} 
	bool operator < (const seg& xxx) const {
		return x < xxx.x;//从左向右, 按顺序扫, 所以需要排序 
	}
} s[MAX<<1];
struct node{
	int x, y, l;
	bool operator < (const node& xxx) const {
		return x < xxx.x ;
	}
}a[MAX];

void push_up(int o) { mx[o] = max(mx[o<<1], mx[o<<1|1]); }
void pushtag(int o, int l, int r, int v) { addv[o] += v, mx[o] += v;};
void push_down(int o, int l, int r) {
	if(addv[o] == 0) return ;
	addv[o<<1] += addv[o];
	mx[o<<1] += addv[o];
	addv[o<<1|1] += addv[o];
	mx[o<<1|1] += addv[o];
	addv[o] = 0;
}
void optdate(int o, int l, int r, int ql, int qr, int v) {
	if(ql <= l && r <= qr) { pushtag(o, l, r, v); return ;}
	push_down(o, l, r);
	int mid = (l+r)>>1;
	if(ql <= mid) optdate(o<<1, l, mid, ql, qr, v);
	if(qr > mid) optdate(o<<1|1, mid+1, r, ql, qr, v);
	push_up(o);
}

int main() {
	int T;
	scanf("%d",&T);
	while(T--) {
		scanf("%d%d%d", &n, &w, &h);
		memset(mx, 0, sizeof(mx));
		memset(day, 0, sizeof(day));
		int x,y,l;
		for(int i = 1; i <= n; i++) {
			scanf("%d%d%d",&x,&y,&l);//亮度l 
			s[i] = seg(x, y, y+h-1, l);
			day[i] = y;
			s[i+n] = seg(x+w-1, y, y+h-1, -l);//-l表示出 
			day[i+n] = y+h-1;
		}
		n <<= 1;//两倍 
		sort(day+1, day+1+n);
		int tot = 1;
		for(int i = 2; i <= n; i++) if(day[i] != day[tot]) day[++tot] = day[i];
		/*unique的使用: 
		sort(day+1,day+1+n);//排序 
        int tot=unique(day+1,day+1+n)-day-1;//去重 */
        sort(s+1,s+1+n);//根据横坐标排序 
		//build();刚开始就是空的 
		int ans = 0;
		for(int i = 1; i <= n; i++) {
			int l = lower_bound(day+1, day+1+tot, s[i].y1 ) - day;//二分查找边界 
			int r = lower_bound(day+1, day+1+tot, s[i].y2 ) - day;//找到y1和y2,求出区间最大值 
			if(l > r) swap(l, r);
			optdate(1, 1, tot, l, r, s[i].l );//下次再扫到右边界时就会在原区间加上-l,即删除; //
			ans = max(ans, mx[1]);
		}
		printf("%d
",ans);
	}
	
}
原文地址:https://www.cnblogs.com/tyner/p/11251690.html