扫描线

Posted hansue

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了扫描线相关的知识,希望对你有一定的参考价值。

题目链接

考虑将每个矩形看做两次操作,分别是在$Y$轴上叠加线段和去除线段。按$X$坐标排序后依次访问扫过。

注意:本题中,坐标表示的是直角坐标系的整点坐标,也即我们计算的是连续的面积,不过这里矩形都是水平的。

那么离散化一波,每个离散点表示到它的后继之间的线段。

考虑使用线段树,维护“覆盖计数”和“有效线段长”。

本题的线段树虽然是区间修改区间查询,但注意到本题的区间查询实际上都只在询问全区间,也即,如果写传统的区间询问函数,访问完根节点,都不会往下递归。

那么不需要做“覆盖计数”的懒标记了,只要每次精准定位$O(log n)$个区间修改,注意每次回溯都维护一下“有效线段长”就行了。

具体方案是,如果一个区间被全覆盖了,那么它的“有效线段长”就是区间原长度。否则就是两个子区间的答案相加。

这里有一个细节,当你访问未被覆盖或者正好被去除覆盖的叶子结点时,你会尝试访问它的子区间。这显然是错误的,要么特判该情况时“有效线段长”为$0$,要么将数组大小翻倍来无视该情况。

 

代码(100分):

技术图片
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<vector>
#include<queue>
#include<map>
#include<set>
#define IL inline
#define RG register
#define _1 first
#define _2 second
using namespace std;
typedef long long LL;
const int N=2e5;

    int n,m,a[N+3];
    
struct Lne{
    int d,x,y,t;
    Lne(int d=0,int x=0,int y=0,int t=0)
        :d(d),x(x),y(y),t(t){}
}p[N+3];

IL bool operator<(Lne x,Lne y){
    return x.d<y.d;
}

IL int sol(int x){
    int l=1,r=m,mid,ans;
    while(l<=r){
        mid=(l+r)>>1;
        if(x<=a[mid]){
            r=mid-1;    ans=mid;
        }
        else 
            l=mid+1;
        
    }
    return ans;
    
}

IL void discrete(){
    n<<=1;
    sort(a+1,a+n+1);
    m=1;
    for(int i=2;i<=n;i++)
    if(a[i]!=a[i-1])
        a[++m]=a[i];
    
    for(int i=1;i<=n;i+=2){
        p[i].x=p[i+1].x=sol(p[i].x);
        p[i].y=p[i+1].y=sol(p[i].y);
        
    }
    
}

    int c[N*4+3],s[N*4+3];
    
IL int ls(int i){return i<<1;}
IL int rs(int i){return i<<1|1;}
    
IL void merge(int i,int l,int r){
    if(c[i])
        s[i]=a[r+1]-a[l];
    else 
        if(l==r)
            s[i]=0;
        else 
            s[i]=s[ls(i)]+s[rs(i)];
        
}

void mdf(int i,int l,int r,int ql,int qr,int x){
    if(ql<=l&&r<=qr){
        c[i]+=x;
        merge(i,l,r);
        return ;
        
    }
    
    int mid=(l+r)>>1;
    if(ql<=mid)
        mdf(ls(i),l,mid,ql,qr,x);
    if(qr>mid)
        mdf(rs(i),mid+1,r,ql,qr,x);
    merge(i,l,r);
    
}

int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        int x1,y1,x2,y2;
        scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
        a[i*2-1]=y1;
        a[i<<1]=y2;
        p[i*2-1]=Lne(x1,y1,y2,1);
        p[i<<1]=Lne(x2,y1,y2,-1);
        
    }
    
    discrete();
    sort(p+1,p+n+1);
    LL ans=0;
    for(int i=1;i<n;i++){
        mdf(1,1,m,p[i].x,p[i].y-1,p[i].t);
        ans+=1LL*s[1]*(p[i+1].d-p[i].d);
        
    }
    
    printf("%lld",ans);

    return 0;

}
View Code

 

以上是关于扫描线的主要内容,如果未能解决你的问题,请参考以下文章

片段创建的 Intent 不会触发 onNewIntent

Zxing QR扫描仪onActivityResult未在片段中调用[重复]

在 zxing 片段库中打开/关闭手电筒

扫描算法(SCAN)——磁盘调度管理

片段中的视频视图与另一个片段重叠

通过片段之间的导航传递数据android studio