[蓝桥杯][2017年第八届真题]油漆面积

扫描线。

我们取出(N)个矩形的左右边界。若一个矩形的两个对角顶点坐标为((x_1,y_1))((x_2,y_2)),则左边界记为四元组((x_1,y_1,y_2,1)),右边界记作四元组$(x_2,y_1,y_2,-1)。把这(2N)个四元组按照(x)递增排序。

逐一扫描排序后的(2N)个四元组,设当前四元组为((x,y_1,y_2,mark))。我们把数组(tr)(tr[y_1],tr[y_1+1],cdots,tr[y_2-1])这些值都加上(mark),相当于覆盖了$[y_1,y_2]这个区间。

值得说明的是,四元组中的(y_1,y_2)都是坐标,是一个“点”。我们需要维护的是扫描线上每一段被覆盖的次数及其西长度,对“点”的覆盖次数进行统计是没有意义的。因此,我们把(tr)数组中的每个值(tr[i])定义成扫描线上一个区间,即([i,i+1])的覆盖次数,四元组((x,y_1,y_2))(tr[y_1 sim y_2-1])产生影响。

const int N=10010;
struct Seg
{
    int x,y1,y2;
    int mark;
    bool operator<(const Seg &W) const
    {
        return x < W.x;
    }
}seg[N<<1];
struct Node
{
    int l,r;
    int cnt;
    int len;
}tr[N<<2];
int n;
int cnt;

void pushup(int u)
{
    if(tr[u].cnt) tr[u].len=tr[u].r-tr[u].l+1;
    else
    {
        if(tr[u].l == tr[u].r) tr[u].len=0;//特判叶子结点,叶子结点不能借助左右儿子更新,否则会Segmentation Fault
        else tr[u].len=tr[lc].len+tr[rc].len;
    }
}

void build(int u,int l,int r)
{
    tr[u]={l,r};
    if(l == r) return;
    int mid=l+r>>1;
    build(lc,l,mid);
    build(rc,mid+1,r);
}

void modify(int u,int l,int r,int mark)
{
    if(tr[u].l >= l && tr[u].r <= r)
    {
        tr[u].cnt+=mark;
        pushup(u);
    }
    else
    {
        int mid=tr[u].l+tr[u].r>>1;
        if(l<=mid) modify(lc,l,r,mark);
        if(r>mid) modify(rc,l,r,mark);
        pushup(u);
    }
}

int main()
{
    cin>>n;

    for(int i=0;i<n;i++)
    {
        int x1,y1,x2,y2;
        cin>>x1>>y1>>x2>>y2;
        seg[cnt++]={x1,y1,y2,1};
        seg[cnt++]={x2,y1,y2,-1};
    }

    sort(seg,seg+cnt);

    build(1,0,10000);

    int res=0;
    for(int i=0;i<cnt;i++)
    {
        if(i) res+=tr[1].len*(seg[i].x-seg[i-1].x);
        modify(1,seg[i].y1,seg[i].y2-1,seg[i].mark);
    }
    cout<<res<<endl;
    //system("pause");
    return 0;
}
原文地址:https://www.cnblogs.com/fxh0707/p/14588742.html