线段树+扫描线(基本原理)

      这一部分是线段树的一个难点了,这写天做了这么多的这方面的题,一直是稀里糊涂的搞不太明白,但是又得理解,看到了一个别人转载的讲解,贴在这里以便回顾(侵删)。

  扫描线法:

      假想有一条扫描线,从左往右(从右往左),或者从下往上(从上往下)扫描过整个多边形(或者说畸形。。多个矩形叠加后的那个图形)。如果是竖直方向上扫描,则是离散化横坐标,如果是水平方向上扫描,则是离散化纵坐标。下面的分析都是离散化横坐标的,并且从下往上扫描的

扫描之前还需要做一个工作,就是保存好所有矩形的上下边,并且按照它们所处的高度进行排序,另外如果是上边我们给他一个值-1,下边给他一个值1,我们用一个结构体来保存所有的上下边

struct segment 
{ 
double l,r,h; //l,r表示这条上下边的左右坐标,h是这条边所处的高度 
int f; //所赋的值,1或-1 
}

接着扫描线从下往上扫描,每遇到一条上下边就停下来,将这条线段投影到总区间上(总区间就是整个多边形横跨的长度),这个投影对应的其实是个插入和删除线段操作。还记得给他们赋的值1或-1吗,下边是1,扫描到下边的话相当于往总区间插入一条线段,上边-1,扫描到上边相当于在总区间删除一条线段(如果说插入删除比较抽象,那么就直白说,扫描到下边,投影到总区间,对应的那一段的值都要增1,扫描到上边对应的那一段的值都要减1,如果总区间某一段的值为0,说明其实没有线段覆盖到它,为正数则有,那会不会为负数呢?是不可能的,可以自己思考一下)。

每扫描到一条上下边后并投影到总区间后,就判断总区间现在被覆盖的总长度,然后用下一条边的高度减去当前这条边的高度,乘上总区间被覆盖的长度,就能得到一块面积,并依此做下去,就能得到最后的面积

(这个过程其实一点都不难,只是看文字较难体会,建议纸上画图,一画即可明白,下面献上一图希望有帮助) 

线段树+扫描线(基本原理)_第1张图片

   


      这里还有做的几道题。    

      二维线段树 (区间并) 查询被覆盖的面积  点击打开链接  简单题

      模板!!!

代码如下:

#include
#include
#include
#include
using namespace std;
const int MAX=210;
int N;
double x[MAX<<2];
struct Node
{
    double l, r;
    double h;
    int flag;
}node[MAX<<2];
int cmp(Node a, Node b)
{
    return a.h>1;
        if(x[mid]>val)
            r=mid-1;
        else if(x[mid]>1;
    build(rt<<1, left, mid);
    build(rt<<1|1, mid+1, right);
}
void pushUp(int rt)
{
    if(tree[rt].cnt)//非0,整段覆盖
        tree[rt].len=x[tree[rt].r+1]-x[tree[rt].l];
    else if(tree[rt].l==tree[rt].r)//叶子
        tree[rt].len=0;
    else//部分覆盖
        tree[rt].len=tree[rt<<1].len+tree[rt<<1|1].len;
}
void update(int rt, int left, int right, int val)
{
    if(left<=tree[rt].l && tree[rt].r<=right)//全部包含
    {
        tree[rt].cnt+=val;
        pushUp(rt);
        return ;
    }
    int mid=(tree[rt].l+tree[rt].r)>>1;
    if(left<=mid)
        update(rt<<1, left, right, val);
    if(right>mid)
        update(rt<<1|1, left, right, val);
    pushUp(rt);//计算该区间被覆盖的总长度
}
int main()
{
    int K=0;
    int l, r;
    double x1, x2, y1, y2;
    while(~scanf("%d", &N), N)
    {
        int cnt=0;
        for(int i=1; i<=N; i++)
        {
            scanf("%lf%lf%lf%lf", &x1, &y1, &x2, &y2);
            x[++cnt]=x1;
            node[cnt].l=x1;
            node[cnt].r=x2;
            node[cnt].h=y1;
            node[cnt].flag=1;//下边
            x[++cnt]=x2;
            node[cnt].l=x1;
            node[cnt].r=x2;
            node[cnt].h=y2;
            node[cnt].flag=-1;//上边
        }
        sort(x+1, x+cnt+1);//排序
        sort(node+1, node+cnt+1, cmp);
        /*
        int M=1;
        for(int i=1; i





      二维线段树 (区间交) 查询两次的面积    点击打开链接  难题

代码如下:

#include
#include
#include
#include
#include
#include
using namespace std;
const int MAX=2010;
int T, N, cnt;
double x[MAX<<2];
struct Node
{
    double l, r;
    double y;
    double flag;
}node[MAX<<2];
int cmp(Node a ,Node b)
{
    return a.y>1;
        if(x[mid]val)
            r=mid-1;
        else
            break;
    }
    return mid;
}

void build(int rt, int left, int right)
{
    tree[rt].l=left;
    tree[rt].r=right;
    tree[rt].add=tree[rt].len1=tree[rt].len2=0;//全置0
    if(left==right)//叶子
    {
        return ;
    }
    int mid=(left+right)>>1;
    build(rt<<1, left, mid);
    build(rt<<1|1, mid+1, right);
}
void pushUp(int rt)
{
    if(tree[rt].add>=2)
    {
        tree[rt].len1=tree[rt].len2=x[tree[rt].r+1]-x[tree[rt].l];
    }
    else if(tree[rt].add==1)
    {
        tree[rt].len1=x[tree[rt].r+1]-x[tree[rt].l];
        if(tree[rt].l==tree[rt].r)
            tree[rt].len2=0;
        else
            tree[rt].len2=tree[rt<<1].len1+tree[rt<<1|1].len1;
    }
    else
    {
        if(tree[rt].l==tree[rt].r)
            tree[rt].len1=tree[rt].len2=0;
        else
        {
            tree[rt].len1=tree[rt<<1].len1+tree[rt<<1|1].len1;
            tree[rt].len2=tree[rt<<1].len2+tree[rt<<1|1].len2;
        }
    }
}
void update(int rt, int left, int right, int val)
{
    if(tree[rt].l==left && tree[rt].r==right)
    {
        tree[rt].add+=val;
        pushUp(rt);
        return ;
    }
    int mid=(tree[rt].l+tree[rt].r)>>1;
    if(right<=mid)
        update(rt<<1, left, right, val);
    else if(left>mid)
        update(rt<<1|1, left, right, val);
    else
    {
        update(rt<<1, left, mid, val);
        update(rt<<1|1, mid+1, right, val);
    }
    pushUp(rt);
}
int main()
{
    double x1, x2, y1, y2;
    int l, r;
    cin>>T;
    while(T--)
    {
        cnt=0;
        memset(node, 0, sizeof(node));
        memset(tree, 0, sizeof(tree));
        cin>>N;
        for(int i=1; i<=N; i++)
        {
            cin>>x1>>y1>>x2>>y2;
            x[++cnt]=x1;
            node[cnt].l=x1;
            node[cnt].r=x2;
            node[cnt].y=y1;
            node[cnt].flag=1;//左边
            x[++cnt]=x2;
            node[cnt].l=x1;
            node[cnt].r=x2;
            node[cnt].y=y2;
            node[cnt].flag=-1;//右边
        }
        //cout<<"..........   cnt=  "<


      二维线段树  求固定面积矩形(W*H)内星星和的最大值   点击打开链接

代码如下:

#include
#include
#include
#include
using namespace std;
const int MAX=20010;
typedef long long LL;
LL xis[MAX<<2];
struct Node
{
    LL l, r;
    LL h;
    LL s;
    bool operator <(const Node & a) const
    {
        if(h==a.h)
            return s>1;
        if(xis[mid]>val)
            r=mid-1;
        else if(xis[mid]>1;
    build(rt<<1, left, mid);
    build(rt<<1|1, mid+1, right);
}
void update(int L, int R, int c, int rt)
{
    if(L<=tree[rt].l && tree[rt].r<=R)
    {
        tree[rt].cnt+=c;
        tree[rt].sum+=c;
        return ;
    }
    pushDown(rt);
    int mid=(tree[rt].l+tree[rt].r)>>1;
    if(L<=mid)
        update(L, R, c, rt<<1);
    if(mid>N>>W>>H && N && W && H)
    {
        int m=0;
        LL ans=-1;
        LL x, y, c;
        int l, r;
        for(int i=1; i<=N; i++)
        {
            cin>>x>>y>>c;
            xis[++m]=x;
            node[m].l=x;
            node[m].r=x+W;
            node[m].h=y;
            node[m].s=c;
            xis[++m]=x+W;
            node[m].l=x;
            node[m].r=x+W;
            node[m].h=y+H;
            node[m].s=-c;
        }
        build(1, 1, m);
        sort(xis+1, xis+m+1);
        sort(node+1, node+m+1);
        for(int i=1; i<=m; i++)
        {
            l=findPos(1, m, node[i].l);
            r=findPos(1, m, node[i].r)-1;
            if(l<=r)
                update(l, r, node[i].s, 1);
            ans=max(ans, tree[1].sum);
        }
        cout<



你可能感兴趣的:(线段树+扫描线(基本原理))