n^2扫描线

· · 算法·理论

PS:

扫描线(实则为一种思想)

实现过程:

以从左往右扫的矩形面积并为例:

预处理

  1. 将每个矩形的两个 y 坐标离散出来。
  2. 记录每个宽的 x 坐标与 y1y2 坐标,并像处理“()”一样将它们标记为 \pm 1
  3. 要让线从左至右扫到每条宽,所以宽按 x 升序排序。
    struct Weith{
        ll x,y1,y2,v;
    }W[N<<1];
    bool cmpW(Weith a,Weith b){
        if(a.x==b.x) return a.v>b.v;//此行有必要,具体见周长并代码
        return a.x<b.x;
    }
    int main(){
        for(int i=1,x1,x2,y1,y2;i<=n;i++){
            cin>>x1>>y1>>x2>>y2;
            //如图中蓝线:左边的宽标为1,进入了矩形;右边的宽标为-1,退出了该矩形
            W[i*2-1]={x1,y1,y2,1};
            W[i*2]={x2,y1,y2,-1};
            //如图中红线:离散y
            y[i*2-1]=y1;
            y[i*2]=y2;
        }
        sort(W+1,w+2*n+1,cmpW);
        sort(y+1,y+2*n+1)
        ll tot =unique(y+1,y+2*n+1)-(y+1);
    }

扫描操作实现

  1. 从左往右枚举每条宽 W_i,判断每个区间 c_j 是否被覆盖。
  2. 如果该区间被覆盖,统计答案。
    1. 该步骤需在步骤 2 之前进行
    2. 如图,枚举到 x_3 时统计的面积为 x_2x_3 之间的部分。若先更改 c_jy4y5 之间的绿色阴影部分可能无法统计。
    for(int i=1;i<=2*n;i++){
        for(int j=1;j<tot;j++){
            //统计答案
            if(c[j]>0){
                //x3处重合的两条宽并不会将绿色阴影部分统计两次,因为当枚举到i+1时W[i+1].x-W[i].x=0
                //每个cj对应的范围为 y[j] ~ y[j+1]
                ans+=(W[i].x-W[i-1].x)*(y[j+1]-y[j]);
            }
            //更新cj
            if(W[i].y1<=c[j]&&c[j]<W[i].y2){//下闭上开,不能重复统计
                c[j]+=W[i].v;
            }
        }
    }

矩形周长并:

    struct Weith{
        ll x,y1,y2,v;
    }W[N<<1];
    struct Length{
        ll y,x1,x2,v;
    }L[N<<1];
    bool cmpW(Weith a,Weith b){
        if(a.x==b.x) return a.v>b.v;
        return a.x<b.x;
    }
    bool cmpL(Length a,Length b){
        if(a.y==b.y) return a.v>b.v;
        return a.y<b.y;
    }