扫描线

首先考虑这样一个问题
给你 n 个矩形的左上角坐标和右下角坐标,求他们的面积并。

我会暴力! 其中 n<=1e5 坐标都是 int 范围内的数。

我会容斥! 显然会 TLE
所以我们需要考虑一种更快速更容易维护的方式。


扫描线,顾名思义就是一条平行于坐标轴的线从第一个矩形的左边一直扫描到最后一个矩形的右边。

考虑维护每次扫描线碰到的矩形的边长,只需要将这次碰到的边长乘上这次碰到的边的横坐标与上次碰到的边的横坐标的差就可以算出两端扫描线之间的距离。

那么如何维护扫描线扫到的矩形的边长呢?
可以使用一个变量记录这条边是矩形的左边还是右边,因为可以发现的是,扫描线的长度是随着每次碰到一条矩形的边而不断变化的,所以只需要一个变量 cover 来记录某个点是否被扫描线所覆盖,如果当前的扫描线扫到的边是左边就 cover++,是右边就 cover--

所以每次扫描线扫到一条新的边,就只需要更改所有扫描线覆盖的边的 cover 值即可。
这种操作不就是线段树的区间修改操作吗?
整理一下思路,首先我们在 y 轴上建立线段树维护扫描线覆盖的矩形的边长,每次进行区间修改并累加答案即可。
需要注意的是,由于坐标的数值很大,所以我们需要对坐标进行离散化操作。

struct Line{
    int x;
    int y1,y2;
    int state;
    bool operator < (Line a){ return x < a.x; }
}line[N];

struct Node{
    int l,r;
    int cover;
    long long len;
}tree[N<<3];

inline void push_up(int p){
    if(tree[p].cover) tree[p].len = tree[p].r - tree[p].l;
    else tree[p].len = tree[p<<1].len+tree[p<<1|1].len;
}

void build(int p,int l,int r){
    tree[p].l = v[l] , tree[p].r = v[r];
    if(r - l <= 1) return;
    int mid = l + r >>1;
    build(p<<1,l,mid);
    build(p<<1|1,mid,r);
}
void modify(int p,int l,int r,int k){
    if(l <= tree[p].l && tree[p].r <= r){
        tree[p].cover += k;
        push_up(p);
        return;
    }
    if(l < tree[p<<1].r) modify(p<<1,l,r,k);
    if(r > tree[p<<1|1].l) modify(p<<1|1,l,r,k);
    push_up(p);
}
int main(){
    int n = read();
    for(int i=1;i<=n;i++){
		int a = read() , b = read() , c = read() , d = read();
		v[i] = b,v[n+i] = d;
        line[i] = (Line){a,b,d,1} , line[n+i] = (Line){c,b,d,-1};
    }
    sort(v+1,v+(n<<1)+1);
    sort(line+1,line+(n<<1)+1);
    build(1,1,n<<1);
    unsigned long long ans = 0;
    for(int i=1;i<=n<<1;i++){
        ans += tree[1].len * (line[i].x - line[i-1].x);
        modify(1,line[i].y1,line[i].y2,line[i].state);
    }
    printf("%llu\n",ans);
	return 0;
}
posted @   0xFF_qwq  阅读(45)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示