洛谷 P5490 【模板】扫描线

nn 个矩形的面积并

思路:扫描线

#include<iostream>
#include<cstdio>
#include<algorithm>
#define ls (x<<1)
#define rs (x<<1|1)
#define MAXN 1000010
using namespace std;
typedef long long ll;
int n,x1,x2,y1,y2,X[MAXN<<1];
struct Scan{
    int l,r,h,mark;
    bool operator<(const Scan&r)const{
        return h<r.h;
    }
}line[MAXN<<1];
struct SegTree{
    int l,r,sum,len;
}tree[MAXN<<2];
void build(int x,int l,int r){ // l,r 实际对应 X[l]~X[r+1] 这条边
    tree[x].l=l,tree[x].r=r; // 离散化后的下标范围
    tree[x].len=0,tree[x].sum=0; // 初始化区间长度为 0, sum 表示 mark 的累加
    if(l==r)return;
    int m=l+(r-l)/2;
    build(ls,l,m);build(rs,m+1,r);
}
void pushup(int x){
    int l=tree[x].l,r=tree[x].r;
    if(tree[x].sum)tree[x].len=X[r+1]-X[l];
    else tree[x].len=tree[ls].len+tree[rs].len;
}
void update(int x,int L,int R,int c){ // L,R 表示该扫描线在横轴上的左右坐标,
    // c 表示扫描线在矩形下边(1),还是上边(-1)
    int l=tree[x].l,r=tree[x].r;
    if(X[r+1]<=L||R<=X[l])return;
    if(L<=X[l]&&X[r+1]<=R){
        tree[x].sum+=c;
        pushup(x);return;
    }
    update(ls,L,R,c);update(rs,L,R,c);
    pushup(x);
}
int main(){
#ifdef WINE
    freopen("data.in","r",stdin);
#endif
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
        X[2*i-1]=x1,X[2*i]=x2; // 横坐标离散化
        line[2*i-1]=(Scan){x1,x2,y1,1}; // 下边权值为正
        line[2*i]=(Scan){x1,x2,y2,-1};  // 上边权值为负
    }
    n<<=1;
    sort(line+1,line+n+1); // 扫描线根据从下到上进行排序
    sort(X+1,X+n+1); // 横坐标从左到右排序
    int tot=unique(X+1,X+n+1)-X-1; // 横坐标去重,unique 返回的是去重数组最后元素的后一位指针
    build(1,1,tot-1); // 构造线段树
    ll res=0;
    for(int i=1;i<n;i++){ // 从下到上遍历所有扫描线,最上面的那条不用考虑
        update(1,line[i].l,line[i].r,line[i].mark); // 更新该扫描线对线段树的影响
        res+=ll(tree[1].len)*(line[i+1].h-line[i].h);
    }
    printf("%lld\n",res);
    return 0;
}
posted @ 2020-06-24 08:59  winechord  阅读(116)  评论(0编辑  收藏  举报