[Study notes] scan lines

I. About scan line

  And it is based on the perimeter and area and seeking algorithm.

  Note that the scanning line is a line that does not exist.

  Suppose there is a down the scanning line above the (or swept left to right) from the bottom of a pattern, the pattern is cut line obtained by analyzing the scanning line can be obtained desirable results.

 

II. Scan line and find the area (because I do not make plans, the following pictures are from Los solution to a problem Valley)

  We look at this thing.

  

  We simulate a scan line, from the bottom to sweep the entire plane.

  This will stop the scanning lines to update something in the face of the lateral segment. Then the entire graph can identify four line segments.

  Figure:

  What we need to update it? Of course, the length of the line segment calculated.

  So the first thing we have to be recorded is determined. About the coordinates of the endpoints of each line segment.

  Then we put these coordinates into an array, called X [] it.

  This sort of thing is needed. Specific reasons, please read on.

  So we consider the scanning lines monotonous up the process, how do you know where there is an area, where it is empty?

  We think there is a rectangle on the bottom and at the end of the scan line up the monotony of the process, always the first encounter at a rectangular base, then meet on the bottom, then this graphic of a scan line area was swept up.

  So we have to record a second thing, to assign a weight on each lateral line, if the substrate is assigned to 1 if the substrate is assigned a value of -1 sweep the right, so that the scan lines have a portion we want to calculate the area.

 

  Then we consider the problem area and we know that two rectangles intersect part of the area can only be calculated once.

  Intersection of two rectangles, a rectangular lateral edge of at least one point on the edge of another rectangle.

  Then as FIG X [1] and the left and right segment endpoints as composition X [3] can be divided into two parts, X [1] and X [2] and a composition segment X [2] and X [3] composed of segments.

  Thus the pattern was divided into three parts laterally transverse edge 4, a longitudinal section 3 is divided into four longitudinal edges.

  But the scan lines are swept from the bottom up, can only deal with the divided transverse edges, longitudinal side how to do it?

  

  Then we can think of using the tree line.

 

  We segment tree with each node to store a segment, this segment is not necessarily cut into about a group of endpoints may be left - the left or right point - the right endpoint, the endpoint may be non around the same group.

  The following diagram, we can build like a tree line on this graph.

  For the above sentence we can clearly see by the chart.

  当一条线段被扫描线扫到的时候,立即更新线段树每个节点维护的线段的覆盖长度和权值。

  比如扫到最下面这条线段的时候,线段树1,2节点维护的线段覆盖长度和权值就会被更新。

  扫到下数第二条线段的时候,1,2,3,5,6节点维护的线段覆盖长度和权值就会被更新。

  那么不难看出线段树所维护的左右节点实际上是线段的编号,另外维护线段覆盖长度和权值。

  这样扫描线扫有权值的部分就有我们要计算的面积,那么就更新线段覆盖长度。

  那么对于每一条线段我们也记录它的左右端点和纵坐标以及权值,按照纵坐标优先升序排序,保证扫描线从下向上扫。

  

  还剩下一个问题,X[]数组。

  不难看出这个X[]数组排序的意义是为了方便在线段树更新节点线段覆盖长度的时候直接减就行了。

  另外,相同的X[]我们其实只需要一个就够了,所以我们要离散化处理。

  这样最后就可以进行计算面积了。

  S=Σ线段覆盖长度*扫过的高度,即纵坐标的差。

 

  代码如下:(我觉得上面没理解透彻的同学看下代码也应该理解得差不多了吧)

  题目:洛谷【模板】扫描线

#include<bits/stdc++.h>
using namespace std;
struct Segment//从下向上的扫描线 那么就是横向的线段 
{
    long long l,r,h;//每一条线段的左端点坐标 右端点坐标 纵坐标。
              //其中纵坐标的意义是在读到它对应的线段 也就是对于一个矩形的下底 读到它的上底时更改val 
    int val;//val的意义就是在下底使这一个线段权值加1 表示扫描线向下扫的时候 保证这条线段的下方是覆盖的
            //那么在遇到上底时权值为-1 也就是说覆盖结束了 
            //对于整个平面 扫描线所加的面积就是扫过的 并且有权值的位置 
    bool operator < (const Segment &k) const
    {
        return h<k.h;
    }//重载运算符 使得纵坐标小的线段 也就是每个矩形的下底优先被扫描线读到 
}Seg[800010];//空间要开足够大 每个矩形最少有两条线段 所以最少应该开两倍 
struct SegTree//线段树部分
{
    int l,r;//线段树的左右节点 分别用来存储 一条横向线段的编号
            //这里注意 线段树每个节点存储的不是线段的左右端点 而是对于一对左右端点截出的线段
            //并且 这里的左右节点并不严格在输入时对应一条线段 但它们之间一定是有一条线段
            /*就比如说 3号节点所存的线段可能是第二条线段的右端点和第三条线段的右端点所截成的线段 
              因为第二条线段和第三条线段的纵坐标相同 所以它们有交集但不完全重合 可能会出现这样的情况*/ 
    int sum;//这里的sum表示线段树节点的权值 也就是说被覆盖的次数
            //但由于是求面积并 所以被覆盖一次还是两次并没有区别 只是有权值和没权值的区别
            //有权值的线段 在扫描线扫过的时候就会计算面积 否则就不会计算面积 
    long long len;//这是每一个线段树节点所代表的线段的长度 计算面积时使用 
}Tree[1600010];//空间要开足够大  一个线段树四倍 最好开到八倍会好一点 
long long X[800010];
long long n,x1,x2,yy,y2,ans;
void Build(int k,long long l,long long r)
{
    Tree[k].l=l;
    Tree[k].r=r;
    Tree[k].sum=0;//扫描线没来之前没有权值 权值变更是在扫描线到达某一条线段时修改 
    Tree[k].len=0;//同上 
    if(l==r)
    {
        return;
    }
    int mid=l+r>>1;
    Build(k<<1,l,mid);
    Build(k<<1|1,mid+1,r);
}
void pushup(int k)
{
    int l=Tree[k].l,r=Tree[k].r;
    if(Tree[k].sum)//这就是上面所说的 只有被覆盖和没被覆盖的区别
    {
        Tree[k].len=X[r+1]-X[l];//如果线段被扫描线扫到了 则更新扫到的线段长度 
    } 
    else
    {
        Tree[k].len=Tree[k<<1].len+Tree[k<<1|1].len;//如果没被扫到 则从以前扫过的线段更新 
    }
}
void update(int k,long long L,long long R,int val)//当扫描线扫到一条线段 执行此操作 L,R分别记录线段的左右端点坐标 val代表这是下底还是上底
{
    int l=Tree[k].l,r=Tree[k].r;
    if(X[r+1]<=L || R<=X[l])//要更新的线段不属于这个线段树节点 算是个剪枝吧 
    {
        return;
    }
    if(L<=X[l] && X[r+1]<=R)
    {
        Tree[k].sum+=val;//更新 如果是下底 那么sum就有了值 代表扫描线往上的地方需要计算面积
                         //如果是上底 那么sum不一定没有值 只是减少 代表它对应的矩形面积计算完毕
        pushup(k);
        return; 
    }
    //由于有剪枝 我们就不用判断了 直接修改子树就好了
    update(k<<1,L,R,val);
    update(k<<1|1,L,R,val);
    pushup(k);
} 
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%lld%lld%lld%lld",&x1,&yy,&x2,&y2);
        X[2*i-1]=x1,X[2*i]=x2;
        Seg[2*i-1]=(Segment){x1,x2,yy,1};//记录线段的信息 
        Seg[2*i]=(Segment){x1,x2,y2,-1};//按照这个操作 开两倍是正常的 
    }
    n<<=1;//线段数是矩形数二倍 
    sort(Seg+1,Seg+n+1);//按照纵坐标升序排序 
    sort(X+1,X+n+1);//相同的横坐标我们只需要一次 
    int cnt=unique(X+1,X+n+1)-X-1;//因此通过离散化来确定不同横坐标的数量 
    Build(1,1,cnt-1);//这里cnt要-1 因为我们cnt代表横坐标的数量 
                     //而线段树存储的是线段数量 根据植树问题 线段数=点数-1
    for(int i=1;i<n;i++) //最后一条边不需要计算和更新了吧
    {
        update(1,Seg[i].l,Seg[i].r,Seg[i].val);
        ans+=Tree[1].len*(Seg[i+1].h-Seg[i].h);//计算面积
    } 
    printf("%lld",ans);
    return 0;
}
View Code

 

三.扫描线求周长并

  待补充~

  其实可以理解一下 是扫描线左右扫一次和上下扫一次~

  

Guess you like

Origin www.cnblogs.com/Rakan-LoveJ/p/11401851.html