[解题记录] 扫描线

题目介绍

原题链接: 2018-2019 ACM-ICPC Pacific Northwest Regional Contest (Div. 1) F-Rectangle
给出n个与坐标轴对齐的矩形,求被奇数个矩形所覆盖的面积。
输入格式:一个n,然后n行,每行4个数字(x_1, y_1, x_2, y_2)分别表示这个矩形的左下角和右上角坐标。坐标范围[0,1e9] 类型int。
经典的扫描线算法可以看这个up的视频,讲的特别好。


思路

  • 扫描线板子题,需要稍微做一些调整。
  • 这里还是先介绍一下经典的扫描线算法。
    经典的扫描线问题是求覆盖面积。模拟一根从左到右扫描的线段,这条线段每碰到一段矩形的边界,就更新一次自己的长度。碰到左边界则加相应长度,有边界则减。这里要注意重叠的部分不要计入。这种区间修改的操作一般可以用线段树实现。每次更新长度前,将当前长度与上一次更新至今的距离(Delta x)相乘,就得出了一个区间的面积。所有区间面积加起来即是答案。
  • 在这道题中,要求的是“被覆盖了奇数个矩形的面积”。所以需要作出一些调整。容易观察到,奇偶这一性质是互相转变的,线段经过左边界和右边界,都一样是改变了某段区域被覆盖次数的奇偶性。因此我们对一般的线段树进行以下调整:
    1. 维护一个cover数组,表示区间内值为1的线段总长度。
    2. 修改其实就是把区间内的线段覆盖奇偶性倒换,容易知道:进行一次更新操作后,区间的cover数就变成len-cover。
    3. lazy标记初始化为0,每次完全更新一个区间,就将lazy^=1,这是因为对同一个区间“翻转”两次,操作会相互抵消。当向下递归时,当前lazy标记为1就说明有还未抵消的“翻转”操作,需要向下传递。

代码

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define lson l,m,rt<<1
#define rson m,r,(rt<<1)|1
const int maxn=1e5+10;
int n,tot;
struct node{
    ll x,low,high;
    bool operator<(const node& tmp)const{
        return x<tmp.x;
    }
};
node nd[maxn<<1];
int y[maxn<<1];//记录所有y坐标,用于离散化
ll cover[maxn*12];//正常应该是8倍,这里因为采用点作为线段树结点,
                //右区间不用m+1,所以需要多出1/2的空间。
bool lz[maxn*12];//表示是否需要down
void pushDown(int rt,int l,int r){
    if(l==r) return;
    if(!lz[rt]) return;
    int ls=rt<<1,rs=rt<<1|1;
    int m=(l+r)>>1;
    cover[ls]=y[m]-y[l]-cover[ls];
    cover[rs]=y[r]-y[m]-cover[rs];
    lz[ls]^=1;
    lz[rs]^=1;
    lz[rt]^=1;
}
void pushUp(int rt){
    cover[rt]=cover[rt<<1]+cover[rt<<1|1];
}
void update(int L,int R,int l=1,int r=tot,int rt=1){
    if(l==r) return ;
    if(L<=y[l] && y[r]<=R){
        cover[rt]=y[r]-y[l]-cover[rt];
        lz[rt]^=1;
        pushDown(rt,l,r);
        return;
    }
    if(l==r-1) return;//一个小区间,不全覆盖就无意义,防止无限递归
    int m=(l+r)>>1;
    if(R>=y[l]){
        pushDown(rt,l,r);
        update(L,R,lson);
    }
    if(L<=y[r]){
        pushDown(rt,l,r);
        update(L,R,rson);
    }
    pushUp(rt);
}

int main(){
    ios::sync_with_stdio(false);
    cin>>n;
    ll a,b,c,d;
    for(int i=1;i<=n;++i){
        cin>>a>>b>>c>>d;
        nd[i].x=a,nd[i].high=d,nd[i].low=b;
        nd[n+i].x=c,nd[n+i].high=d,nd[n+i].low=b;
        y[i]=b,y[n+i]=d;
    }
    sort(nd+1,nd+2*n+1);
    // 离散化
    sort(y+1,y+2*n+1);
    tot=unique(y+1,y+2*n+1)-y-1;
    ll ans=0;
    nd[0].x=nd[1].x;
    for(int i=1;i<=n*2;++i){
        ans+=(nd[i].x-nd[i-1].x)*cover[1];
        if(nd[i].high>nd[i].low)
            update(nd[i].low,nd[i].high);
    }
    ans+=(nd[n*2].x-nd[n*2-1].x)*cover[1];
    cout<<ans<<'
';
    return 0;
}
原文地址:https://www.cnblogs.com/crazyfz/p/12806427.html