再谈扫描线

其实两个多月之前写过一篇,但是那时候的水平怎么能和现在相比,恰好前几天鸽了一道扫描线的题,所以今天来补上;

POJ2482

POJ1151

前面一个是数星星,后面一个是亚特兰蒂斯,两题其实都是属于扫描线的一个模板题啊,数星星那个属于是找最大值的版本,亚特兰蒂斯那个属于是找和的版本;

扫描线其实就是一个平面上有一些线,无非你要求出关于这些线的一个信息罢了。

你先定下一个坐标表示你当前的一个线段树的一个含义,然后把另一个维度当成是时间轴,你就根据另一个维度去操作就好了;

首先如果说那些点的横纵坐标的值域都是不需要离散化的,那么显然是很好操作的,直接乱改就行了;
那么如果值域很离谱的话,我们无非是加上一个离散化是吧;
这里讲一种别人那里看来的很好的一种离散化的方法,就是说我们一般的离散化,在线段树中呢,一个节点的l和r代指的都是数组里面的坐标,然后每次都要进数组再进行一个操作,我从别人那里看来的呢,一个节点的l和r就代指真实的坐标,然后用的时候加上去一下就好了,我个人认为,后面一种写法可能更能贴近我的使用习惯;

然后亚特兰蒂斯那题有个地方要注意,为什么要打上一个tag;
平时求区间和的线段树是不用打tag的,那是因为那是可以随便加的,而这个打上tag是因为不能随便加,有了覆盖之后就不能乱搞了;

附上数星星的代码,这么漂亮的代码,当然是copy的啦;

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
#define int long long
typedef long long ll;
typedef pair<int,int> PII;
const int INF = 0x3f3f3f3f;
const int N = 2e5+7;
const int M = 2007;
int n,w,h,a[N],cnt;

struct Seg{
    int lc,rc;
    int len;
    int lz;
    #define trl(x) tree[x].lc
    #define trr(x) tree[x].rc
    #define tlen(x) tree[x].len
    #define tlz(x) tree[x].lz
}tree[N<<1];
struct node{
    int x,y1,y2,f;
}p[N];
bool cmp(node xa,node yb) {
    return xa.x < yb.x || ( xa.x == yb.x && xa.f < 0 ) ;
}
inline void add(int x,int y,int k) {
	p[cnt].x = x;
	p[cnt].y1 = y;
	p[cnt].y2 = y + h;
	p[cnt++].f = k;
}
inline void build(int p,int l,int r){
	trl(p) = a[l];
	trr(p) = a[r];
	tlz(p) = tlen(p) = 0;
	if(r - l == 1) return;
	int mid = (l+r) >> 1;
	build(p<<1,l,mid);
	build(p<<1|1,mid,r);
}inline void change(int p,int l,int r,int k){
	if(l <= trl(p) && trr(p) <= r){
		tlz(p) += k;
		tlen(p) += k;
		return;
	}
	if(l < trr(p<<1) ) change(p<<1,l,min(trr(p),r),k);
	if(r > trl(p<<1|1))change(p<<1|1,max(l,trl(p)),r,k);
	tlen(p) = max(tlen(p<<1),tlen(p<<1|1)) + tlz(p);
}
int main()
{
	while(scanf("%lld%lld%lld",&n,&w,&h)!=EOF){
		cnt = 1;
		int num = 1;
		for(int i=1;i<=n;i++){
			int x,y,k;
			scanf("%lld%lld%lld",&x,&y,&k);
			add(x,y,k);
			add(x-w,y,-k);
			a[num++] = y;
			a[num++] = y+h;
		}
		sort(a+1,a+num);
		int ass = 0;
		num = unique(a+1,a+num) - (a+1);
		sort(p+1,p+cnt,cmp);
		build(1,1,num);
		for(int i=1;i<=cnt;i++){
			change(1,p[i].y1,p[i].y2,p[i].f);
			if(p[i].f > 0){
				ass = max(ass,tlen(1));
			}
		}
		printf("%lld\n",ass);
	}
	return 0;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值