POJ 1177:线段树 离散化 扫描线

计算畸形区域的周长

比面积的扫描要麻烦些,原因就在不重叠区域的处理,同一段高度可能要重复叠加

所以线段树的结点里要多维护三个东西:

times:区间里不重叠的区间数

比如说第一个区间是1~5,第二个是2~6,,第三个是9~10,那前两个可以合成1~6,和第三个独立,则这个整体的times为2

为了维护times,我们需要lbd和rbd两个变量,分别作为区间左右端点是否被覆盖的标志

有的博客里把这两个变量写成了bool型,这些其实不好,在运算时会带来麻烦,我们直接用int 0和1表示会好些。这样只有在左孩子的rbd*右孩子的lbd!=0的时候才表示两孩子连接时会多一个覆盖次数

感觉扫描线的题目最关键的就是即使把更新后的影响传递上去

还有一个比较关键的东西就是离散化的理解

我的离散化是:把每一个x及其与下一个x之间的区间看作一个整体

比如1 5 8三个点

离散化后,1表示1~5,2表示5~8

应该可以注意到,只需要离散n-1个点,因为最后一个点的右边是没有长度的

所以当线段树中一个结点的表示区间是1~2,那这个区间的长度应该是1~8(1~5+5~8)

具体看代码

#include"cstdio"
#include"queue"
#include"cmath"
#include"stack"
#include"iostream"
#include"algorithm"
#include"cstring"
#include"queue"
#include"map"
#include"set"
#include"vector"
#define ll long long
#define mems(a,b) memset(a,b,sizeof(a))
#define ls pos<<1
#define rs pos<<1|1

using namespace std;
const int MAXN = 1e5+50;
const int MAXQ = 10050;
const int INF = 0x3f3f3f3f;
int n,m;
int x[MAXN];

struct Line{
    int l,r,h,w;
    Line(){}
    Line(int a,int b,int c,int d):l(a),r(b),h(c),w(d){}
}line[MAXN];

bool cmp(Line a,Line b){
    return a.h<b.h;
}

struct Node{
    int l,r;
    int len;
    int cnt;
    int times;
    int w;
    int lbd,rbd;
}node[MAXN<<2];

void build(int l,int r,int pos){
    node[pos].l=l;
    node[pos].r=r;
    node[pos].cnt=node[pos].w=node[pos].times=0;
    node[pos].lbd=node[pos].rbd=0;
    node[pos].len=x[node[pos].r+1]-x[node[pos].l];
    if(l==r) return;
    int mid=(l+r)>>1;
    build(l,mid,ls);
    build(mid+1,r,rs);
}

void pushup(int pos){
    if(node[pos].w>0){
        node[pos].cnt=node[pos].len;
        node[pos].lbd=node[pos].rbd=node[pos].times=1;
    }
    else if(node[pos].l==node[pos].r){
        node[pos].cnt=0;
        node[pos].lbd=node[pos].rbd=node[pos].times=0;
    }
    else{
        node[pos].cnt=node[ls].cnt+node[rs].cnt;
        node[pos].lbd=node[ls].lbd;
        node[pos].rbd=node[rs].rbd;
        node[pos].times=node[ls].times+node[rs].times-node[rs].lbd*node[ls].rbd;
    }
}

int get_pos(int w){
    int low=0,high=m,mid,ans;
    while(low<=high){
        mid=(low+high)>>1;
        if(x[mid]==w) return mid;
        else if(x[mid]>w) high=mid-1;
        else low=mid+1;
    }
}

void update(int l,int r,int pos,int w){
    if(l<=node[pos].l&&node[pos].r<=r){
        node[pos].w+=w;
        pushup(pos);
        return;
    }
    int mid=(node[pos].l+node[pos].r)>>1;
    if(l<=mid) update(l,r,ls,w);
    if(r>mid) update(l,r,rs,w);
    pushup(pos);
}

int main(){
    //freopen("in.txt","r",stdin);
    while(~scanf("%d",&n)){
        for(int i=0;i<2*n;i+=2){
            int x1,y1,x2,y2;
            scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
            line[i]=Line(x1,x2,y1,1);
            line[i+1]=Line(x1,x2,y2,-1);
            x[i]=x1;
            x[i+1]=x2;
        }
        sort(line,line+2*n,cmp);
        sort(x,x+2*n);
        m=0;
        for(int i=1;i<2*n;i++) if(x[i]!=x[i-1]) x[++m]=x[i];
        build(0,m-1,1);
        int ans=0;
        int pre=0;
        for(int i=0;i<2*n-1;i++){
            int l=get_pos(line[i].l);
            int r=get_pos(line[i].r)-1;
            update(l,r,1,line[i].w);
            ans+=2*node[1].times*(line[i+1].h-line[i].h);
            ans+=abs(node[1].cnt-pre);
            pre=node[1].cnt;
        }
        ans+=line[2*n-1].r-line[2*n-1].l;
        printf("%d\n",ans);
    }
    return 0;
}

时间: 2024-10-12 15:51:15

POJ 1177:线段树 离散化 扫描线的相关文章

Picture POJ - 1177 线段树+离散化+扫描线 求交叉图像周长

参考  https://www.cnblogs.com/null00/archive/2012/04/22/2464876.html #include <stdio.h> #include <algorithm> #define LEN 10000 using namespace std; struct Node { int left; int right; int count;//被覆盖次数 //所包含的区间数量,如三条[1,2],[2,3],[4,5]线段被覆盖,则line=2

POJ 1177/HDU 1828 picture 线段树+离散化+扫描线 轮廓周长计算

求n个图矩形放下来,有的重合有些重合一部分有些没重合,求最后总的不规则图型的轮廓长度. 我的做法是对x进行一遍扫描线,再对y做一遍同样的扫描线,相加即可.因为最后的轮廓必定是由不重合的线段长度组成的,这样理论上是对的 要注意处理高度相同的线段,把底边优先处理(在代码里就是f标记为1的线段),因为若是一个矩形的底边和另一个矩形的上边重合,则这个轮廓肯定不能算 不过POJ和HDU的数据好像都比较弱,我没进行上面的细节处理也AC了,不过一个很简单的数据就会不对,所以还是要处理一下才是真正正确的代码 我

POJ 2482 Stars in Your Window 线段树+离散化+扫描线

题面据说很美- 每个星星可以根据在窗口的左下角和右上角两个位置建立两条扫描线,之后就是简单的区间增减和求最大值操作了. 注意要处理在边界上的星星是不算的情况,其实只要把左右边界分别增减一个eps即可. #include <cstdio> #include <cstring> #include <iostream> #include <map> #include <set> #include <vector> #include <

【POJ 2482】 Stars in Your Window(线段树+离散化+扫描线)

[POJ 2482] Stars in Your Window(线段树+离散化+扫描线) Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 11294   Accepted: 3091 Description Fleeting time does not blur my memory of you. Can it really be 4 years since I first saw you? I still remembe

hdu1828 Picture(线段树+离散化+扫描线)两种方法

C - Picture Time Limit:2000MS     Memory Limit:10000KB     64bit IO Format:%I64d & %I64u Submit Status Description A number of rectangular posters, photographs and other pictures of the same shape are pasted on a wall. Their sides are all vertical or

POJ 2528 (线段树+离散化) Mayor&#39;s posters

因为将每个单位都作为一个最小单元的话会爆内存的 所以,将海报的每个端点进行排序,将这些端点最为最小的区间. 毕竟是刚刚接触线段树,理解起来还有些吃力,还是那句话,题做多了慢慢就好了. 萌萌的AC代码君贴上. 1 //#define LOCAL 2 #include <iostream> 3 #include <algorithm> 4 #include <cmath> 5 using namespace std; 6 7 int n; 8 struct CPost 9

HDU 1542 Atlantis 线段树+离散化+扫描线

题意:给出一些矩形的最上角坐标和右下角坐标,求这些矩形的面积并. NotOnlySuccess 线段树专辑中扫描线模板题,弱智的我对着大大的代码看了一下午才搞懂. 具体见思路见注释=.= #include <cstdio> #include <cstring> #include <algorithm> #include <vector> #define lson rt<<1,l,mid #define rson rt<<1|1,mid

poj3277--City Horizon(线段树+离散化+扫描线)

City Horizon Time Limit: 2000MS   Memory Limit: 65536K Total Submissions: 16206   Accepted: 4414 Description Farmer John has taken his cows on a trip to the city! As the sun sets, the cows gaze at the city horizon and observe the beautiful silhouette

HDU 3642 线段树+离散化+扫描线

题意:给你N个长方体的左下角和右上角坐标,问你空间中有多少体积是被大于两个不同的立方体覆盖的.x,y~10^6 z~500 考虑到给的z比较小,所以可以直接枚举z,然后跑二维的扫描线就好. 关于处理被不同的线段覆盖三次的问题,可以维护四个信息,cnt,once,twice,more,然后相互推出结果就好. #include <cstdio> #include <cstring> #include <vector> #include <algorithm> #

hdu1255--覆盖的面积(线段树+离散化+扫描线)

E - 覆盖的面积 Time Limit:5000MS     Memory Limit:32768KB     64bit IO Format:%I64d & %I64u Submit Status Description 给定平面上若干矩形,求出被这些矩形覆盖过至少两次的区域的面积. Input 输入数据的第一行是一个正整数T(1<=T<=100),代表测试数据的数量.每个测试数据的第一行是一个正整数N(1<=N<=1000),代表矩形的数量,然后是N行数据,每一行包含