P5490 扫描线

题目描述

求10 n 个矩形的面积并。

输入格式

第一行一个正整数 n。
接下来 n 行每行四个非负整数 x1, y1, x2, y2,表示一个矩形的左下角坐标为 (x1, y1),右上角坐标为 (x2, y2)。

输出格式

一行一个正整数,表示 n 个矩形的并集覆盖的总面积。

输入输出样例

输入
2
100 100 200 200
150 150 250 255
输出
18000

题目分析

扫描线的模板题。
(解析我真的尝试着写过了,但现在对扫描线的理解不到位,确实是写不出来,以后会补上的)

代码如下
#include <iostream>
#include <cstdio>
#include <cmath>
#include <string>
#include <cstring>
#include <map>
#include <queue>
#include <vector>
#include <set>
#include <algorithm>
#define LL long long
#define PII pair<int,int>
#define x first
#define y second
using namespace std;
const int N=1e6+5,INF=0x3f3f3f3f;
int x[N];					//对所有横坐标的离散化数组
struct Line{
    
    				//扫描线
	int l,r,h;
	int mark;				//矩形的下线标记为1,上线标记为0
	bool operator<(const Line &a) const
	{
    
     return h<a.h; }
}line[N];
struct Node{
    
    				//线段树
	int l,r;
	int sum,len;			//sum:当前的x[l]-x[r+1]区间是否被覆盖
}tr[N*2];					//len:当前的被覆盖的截线总长度
void pushup(int u)
{
    
    	//如果u段被覆盖了,则长度即为x[tr[u].r+1]-x[tr[u].l](此段最大长度)
	if(tr[u].sum) tr[u].len=x[tr[u].r+1]-x[tr[u].l];
	else tr[u].len=tr[u<<1].len+tr[u<<1|1].len;		//否则长度即为其子段长度之和
}
void build(int u,int l,int r)			//建树
{
    
    
	tr[u]={
    
    l,r,0,0};
	if(l==r) return;
	int mid=l+r>>1;
	build(u<<1,l,mid),build(u<<1|1,mid+1,r);
}
void update(int u,int l,int r,int c)
{
    
    
	if(l>=x[tr[u].r+1]||r<=x[tr[u].l]) return;		//如果当前的区间与目标区间无重合部分,不用管
	if(l<=x[tr[u].l]&&x[tr[u].r+1]<=r) tr[u].sum+=c;//如果当前区间被目标区间完全覆盖,则标记此段被覆盖了
	else {
    
    
		update(u<<1,l,r,c);
		update(u<<1|1,l,r,c);
	}
	pushup(u);
}
/*
	注:为什么区间下标用的是l和r+1?
	因为如果直接用l和r会出现l==r的情况,而这种情况的长度为0,无意义
	为了避免这种情况的发生,区间下标采用了l和r+1
*/
int main()
{
    
    
	int n;
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
	{
    
    
		int x1,y1,x2,y2;
		scanf("%d%d%d%d",&x1,&y1,&x2,&y2);		//输入一个矩形
		x[2*i-1]=x1,x[2*i]=x2;					//记录其x坐标的值
		line[2*i-1]={
    
    x1,x2,y1,1};				//记录截线
		line[2*i]={
    
    x1,x2,y2,-1}; 
	}
	n<<=1;
	sort(x+1,x+1+n);					//对x进行离散化
	sort(line+1,line+1+n);				//将所有扫描线按高度进行排序
	int cnt=unique(x+1,x+1+n)-x-1;
	build(1,1,cnt-1);					//所有区间的下标是l和r+1
	LL ans=0;
	for(int i=1;i<n;i++)				//从下往上扫描所有截线
	{
    
    
		update(1,line[i].l,line[i].r,line[i].mark);		//将截线加入线段树
		ans+=(LL)tr[1].len*(line[i+1].h-line[i].h);		//ans+=当前的截线总长度*高度
	}
	printf("%lld\n",ans);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/li_wen_zhuo/article/details/112911947