#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#define mid ((l+r) >> 1)
#define lson (num << 1)
#define rson ((num)<<1 | 1)
#define LL long long
using namespace std;
const int N=1000010;
LL n,X[N<<1],tot,ans;
LL x1,x2,y1,y2;
struct Scanline{
LL l,r,h,mark;
bool operator < (const Scanline &aa) const {
return h < aa.h;
}
}line[N<<1];
struct node{
LL l,r,len,sum;
}tree[N<<2];
void build(int num,int l,int r){
tree[num].l=l; tree[num].r=r;
tree[num].len=tree[num].sum=0;
if(l == r) return;
build(lson,l,mid);
build(rson,mid+1,r);
}
void pushup(int num){
if(tree[num].sum == 0){
tree[num].len=tree[lson].len+tree[rson].len;
return;
}
tree[num].len=X[tree[num].r+1]-X[tree[num].l];
}
void update(int num,int ll,int rr,int c){
int l=tree[num].l,r=tree[num].r;
// 注意,l、r和L、R的意义完全不同
// l、r表示这个节点管辖的下标范围
// 而L、R则表示需要修改的真实区间
if(X[l] >= rr || X[r+1] <= ll) return;
// 这里加等号的原因:
// 假设现在考虑 [2,5], [5,8] 两条线段,要修改 [1,5] 区间的sum
// 很明显,虽然5在这个区间内,[5,8] 却并不是我们希望修改的线段
// 所以总结一下,就加上了等号
if(ll <= X[l] && X[r+1] <= rr){
tree[num].sum+=c;
pushup(num);
return;
}
update(lson,ll,rr,c);
update(rson,ll,rr,c);
pushup(num);
}
int main(){
cin>>n;
for(int i=1; i<=n; i++){
cin>>x1>>y1>>x2>>y2;
X[(i<<1)-1]=x1; X[i<<1]=x2;
line[(i<<1)-1]=(Scanline){x1,x2,y1,1};
line[i<<1]=(Scanline){x1,x2,y2,-1};
}
n<<=1;
sort(line+1,line+n+1);
sort(X+1,X+n+1);
tot=unique(X+1,X+n+1) -X -1;
build(1,1,tot-1);
// 为什么是 tot - 1 :
// 因为右端点的对应关系已经被篡改了嘛…
// [1, tot - 1]描述的就是[X[1], X[tot]]
for(int i=1; i<n; i++){
update(1,line[i].l,line[i].r,line[i].mark);
ans=ans+(line[i+1].h-line[i].h)*tree[1].len;
}
cout<<ans;
return 0;
}
扫描线算法
最新推荐文章于 2023-12-21 16:37:37 发布