Luogu P5490 【模板】扫描线

传送门

作为给dtx的妹子讲题的交换他给我讲的

果然线段树最可爱了w


扫描线可以用来求矩形的面积并…

一个平面上有一些有重叠的矩形,求他们的并集的面积。

直接放网上的图了x

对于每个矩形,将y坐标拆成两个修改操作(插入和删除),从下到上排序;

将x坐标unique离散化,从左到右排序,用线段树维护切割出来的每条线段。

假设有一根从x轴出发,向上移动的线——也就是扫描线!

扫描二维码关注公众号,回复: 7756418 查看本文章

遇到修改时,就在线段树上对应区间加上或减掉线段,答案加上(当前线段长度之和)*(这个修改到下个修改的y坐标之差)。

线段树中维护什么信息?

sum[i]表示区间i的线段总长度。显然,sum[i] = sum[ls]+sum[rs]。

但是对于插入和删除,不能直接修改sum的值,因为一个线段可能被多个矩形覆盖...删除了一个之后还可能有另一个。

lazy[i]表示区间i被几个矩形覆盖。这样,修改的时候直接在lazy上++--就好了。

而且一定是先加再减,所以lazy标记一定不会为负数。

可以发现,每次的查询操作都是查询整个区间。那么只需要考虑上传,不用下传。

若lazy[i]>0,即被覆盖,则sum[i] = 区间i的长度;

否则,sum[i] = sum[ls]+sum[rs]。

注意:

在传统的——维护点的线段树里,(1,4) = (1,2)+(3,4);

但维护线段时,线段(2,3)被忽略掉了。

所以更改一下定义:区间[L,R]对应线段(l,r+1)。这样保证了线段都是长为R-L+1的线段。

修改时,因为没法确定哪里有线段,所以从整个区间开始,两个儿子都要递归。

当完全偏离(L >= r[now]+1 || R <= l[now])时,就可以返回了;

当完全覆盖(L <= l[now] && r[now]+1 <= R)时,就打上lazy。

操作完一个节点并上传,判断lazy、更新sum的值时,

sum[now] = sum[ls] + sum[rs]可能出现越界的情况,因为叶子节点并没有儿子!

所以要么特判一下,要么再开2倍空间…

(哎...想起了第一次写线段树1的时候的事了...)

 .

代码如下

#include<cstdio>
#include<iostream>
#include<cmath>
#include<cstring>
#include<algorithm>
#define MogeKo qwq
using namespace std;
#define ls (now<<1)
#define rs (now<<1|1)

const int maxn = 2e5+10;
int n,x_1,y_1,x_2,y_2,tot;
long long ans;
int X[maxn<<1];
int l[maxn<<2],r[maxn<<2],sum[maxn<<2],lazy[maxn<<2];

struct ScanLine {
    int l,r,h,val;
    bool operator < (const ScanLine & N) const {
        if(h == N.h) return val > N.val;
        return h < N.h;
    }
} line[maxn<<1];

void build(int L,int R,int now) {
    l[now] = L;
    r[now] = R;
    sum[now] = lazy[now] = 0;
    if(L == R) return;
    int mid = (l[now]+r[now]) >> 1;
    build(L,mid,ls);
    build(mid+1,R,rs);
}

void pushup(int now) {
    if(lazy[now])
        sum[now] = X[r[now]+1] - X[l[now]];
    else if(l[now] == r[now])
        sum[now] = 0;
    else
        sum[now] = sum[ls] + sum[rs];
}

void modify(int L,int R,int c,int now) {
    if(L >= X[r[now]+1] || R <= X[l[now]])
        return;
    if(L <= X[l[now]] && X[r[now]+1] <= R) {
        lazy[now] += c;
        pushup(now);
        return;
    }
    modify(L,R,c,ls);
    modify(L,R,c,rs);
    pushup(now);
}

int main() {
    scanf("%d",&n);
    for(int i = 1; i <= n; i++) {
        scanf("%d%d%d%d",&x_1,&y_1,&x_2,&y_2);
        line[2*i-1] = (ScanLine) {x_1,x_2,y_1,1};
        line[2*i] = (ScanLine) {x_1,x_2,y_2,-1};
        X[2*i-1] = x_1;
        X[2*i] = x_2;
    }
    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,tot-1,1);
    for(int i = 1; i < n; i++) {
        modify(line[i].l,line[i].r,line[i].val,1);
        ans += (long long)sum[1] * (line[i+1].h - line[i].h);
    }
    printf("%lld",ans);
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/mogeko/p/11801282.html