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

来源:互联网 发布:c语言入门自学书 编辑:程序博客网 时间:2024/06/06 02:20

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

  扫描线法:

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

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

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

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

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

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

这里写图片描述

   


      这里还有做的几道题。    

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

      模板!!!

代码如下:

#include<stdio.h>#include<string>#include<algorithm>#include<iostream>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<b.h;}struct Tree{    int l, r;    int cnt;    double len;}tree[MAX<<2];int findPos(int l, int r, double val){    int mid;    while(l<=r)    {        mid=(l+r)>>1;        if(x[mid]>val)            r=mid-1;        else if(x[mid]<val)            l=mid+1;        else            break;    }    return mid;}void build(int rt, int left, int right){    tree[rt].l=left;    tree[rt].r=right;    tree[rt].len=0;    tree[rt].cnt=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].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<cnt; i++)//去重  可去可不去            if(x[i]!=x[i-1])                x[M++]=x[i];        */        build(1, 1, cnt);        double ans=0;        for(int i=1; i<=cnt; i++)//拿出每条横线并且更新        {            l=findPos(1, cnt, node[i].l);            r=findPos(1, cnt, node[i].r)-1;            update(1, l, r, node[i].flag);            ans+=tree[1].len*(node[i+1].h-node[i].h);//求面积        }        printf("Test case #%d\nTotal explored area: %.2f\n\n", ++K, ans);    }    return 0;}





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

代码如下:

#include<iostream>#include<cstring>#include<cmath>#include<iomanip>#include<stdio.h>#include<algorithm>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<b.y;}struct Tree{    int l, r;    double add;    double len1, len2;}tree[MAX<<2];int findPos(double val)//找val在x[]中出现的位置{    int l=1, r=cnt;    int mid;    while(l<=r)    {        mid=(l+r)>>1;        if(x[mid]<val)            l=mid+1;        else 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=  "<<cnt<<endl;        sort(x+1, x+cnt+1);        build(1, 1, cnt);        sort(node+1, node+cnt+1, cmp);        double ans=0;        l=findPos(node[1].l);        r=findPos(node[1].r)-1;        update(1, l, r, node[1].flag);        for(int i=2; i<=2*N; i++)        {            ans+=tree[1].len2*(node[i].y-node[i-1].y);            l=findPos(node[i].l);            r=findPos(node[i].r)-1;            update(1, l, r, node[i].flag);        }        printf("%.2lf\n", ans);        //cout<<fixed<<setprecision(2)<<ans<<endl;//输出位数不准确    }    return 0;}


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

代码如下:

#include<iostream>#include<cstring>#include<algorithm>#include<stdio.h>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<a.s;        return h<a.h;    }}node[MAX];struct Tree{    int l, r;    LL cnt;    LL sum;}tree[MAX<<2];int findPos(int l, int r, LL val){    int mid;    while(l<=r)    {        mid=(l+r)>>1;        if(xis[mid]>val)            r=mid-1;        else if(xis[mid]<val)            l=mid+1;        else            break;    }    return mid;}void pushUp(int rt){    tree[rt].sum=max(tree[rt<<1].sum, tree[rt<<1|1].sum);}void pushDown(int rt){    if(tree[rt].cnt)    {        tree[rt<<1].sum+=tree[rt].cnt;        tree[rt<<1].cnt+=tree[rt].cnt;        tree[rt<<1|1].sum+=tree[rt].cnt;        tree[rt<<1|1].cnt+=tree[rt].cnt;        tree[rt].cnt=0;    }}void build(int rt, int left, int right){    tree[rt].l=left;    tree[rt].r=right;    tree[rt].sum=tree[rt].cnt=0;    if(left==right)        return ;    int mid=(left+right)>>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<R)        update(L, R, c, rt<<1|1);    pushUp(rt);}int main(){    int N, W, H;    while(cin>>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<<ans<<endl;    }    return 0;}



原创粉丝点击