题目描述
求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;
}