线段树——扫描线

PKU 1151 && hdu1542  Atlantis  矩形面积并

http://poj.org/problem?id=1151

题意:

给出n个矩形,每个矩形给出左下角坐标,右上角坐标。然后求矩形并的总面积;

思路:

 浮点数先要离散化;然后把矩形分成两条边,上边和下边,对横轴建树,然后从下到上扫描上去,用cnt表示该区间下边比上边多几个,sum代表该区间内被覆盖的线段的长度总和
这里线段树的一个结点并非是线段的一个端点,而是该端点和下一个端点间的线段,所以题目中r+1,r-1的地方可以自己好好的琢磨一下;

这里点到线段的转化不好理解:

我们的线段树的叶子节点表示的是线段,座椅如果我们求1到4的线段长度是实际上是求的1,2,3好线段,所以r要-1,而当我们真正求长度是是X[i + 1] - X[i]才是i线段的长度。

View Code
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <queue>
#include <stack>
#include <set>
#include <map>
#include <string>

#define CL(a,num) memset((a),(num),sizeof(a))
#define iabs(x)  ((x) > 0 ? (x) : -(x))
#define Min(a , b) ((a) < (b) ? (a) : (b))
#define Max(a , b) ((a) > (b) ? (a) : (b))

#define ll __int64
#define inf 0x7f7f7f7f
#define MOD 100000007
#define lc l,m,rt<<1
#define rc m + 1,r,rt<<1|1
#define pi acos(-1.0)
#define test puts("<------------------->")
#define maxn 100007
#define M 100007
#define N 227
using namespace std;
//freopen("din.txt","r",stdin);

struct Seg{
    double l,r,h;
    int mk;
    Seg(){}
    Seg(double L,double R,double H,int MK){
        l = L; r = R; h = H; mk = MK;
    }
    bool operator < (const Seg &tmp) const{
        return h < tmp.h;
    }
}seg[N];

int cnt[N<<2];
double sum[N<<2],X[N];

void pushup(int rt,int l,int r){
    if (cnt[rt]) sum[rt] = X[r + 1] - X[l];//下边比上边多此区间仍满足条件
    else if (l == r) sum[rt] = 0;//单个的一条线段且x长度不满足条件肯定为0
    else sum[rt] = sum[rt<<1] + sum[rt<<1|1];//x不满足条件但其子树可能存在满足条件的
}
void update(int L,int R,int sc,int l,int r,int rt){
    if (L <= l && r <= R){
        cnt[rt] += sc;//更新
        pushup(rt,l,r);
        return ;
    }
    int m = (l + r)>>1;
    if (L <= m) update(L,R,sc,lc);
    if (m < R) update(L,R,sc,rc);
    pushup(rt,l,r);
}
//离散化后二分查找
int bSearch(double val,int n){
    int l = 0;
    int r = n;
    while (l <= r){
        int m = (l + r)>>1;
        if (val == X[m]) return m;
        else if (val < X[m]) r = m - 1;
        else l = m + 1;
    }
    return l;
}
int main(){
    //freopen("din.txt","r",stdin);
    int i;
    int n;
    int cas = 1;
    double x1,y1,x2,y2;
    while (~scanf("%d",&n)){
        if (!n) break;
        int len = 0;
        //按x轴建树,记录横向线段以及x左边
        for (i = 0; i < n; ++i){
            scanf("%lf%lf%lf%lf",&x1,&y1,&x2,&y2);
            X[len] = x1;
            seg[len++] = Seg(x1,x2,y1,1);
            X[len] = x2;
            seg[len++] = Seg(x1,x2,y2,-1);
        }
        
        //离散化
        sort(X,X + len);
        sort(seg,seg + len);
        int k = 1;
        for (i = 1; i < len; ++i){
            if (X[i] != X[i - 1]) X[k++] = X[i];
        }
        //线段树扫描处理
        double res = 0;
        CL(sum, 0); CL(cnt,0);
        for (i = 0; i < len - 1; ++i){
            int l = bSearch(seg[i].l,k - 1);
            int r = bSearch(seg[i].r,k - 1) - 1;//这里讲点转化成线段(难理解)
            if (l <= r) update(l,r,seg[i].mk,0,k - 2,1);
            res += sum[1]*(seg[i + 1].h - seg[i].h);//sum[1]记录整个区间满足条件的x的长度
        }
        printf("Test case #%d\n",cas++);
        printf("Total explored area: %.2lf\n\n",res);
    }
    return 0;
}

 

pku 1177 Picture 矩形周长并

 http://poj.org/problem?id=1177 

题意:

给出n个矩形,每个矩形给出左下角坐标,右上角坐标。然后求矩形周长的并;

思路:

扫描线,按x轴建树,len[]记录横向有效长度,numseg[]记录叔侄方向的有效个数。然后就是扫描一遍过去。

View Code
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <queue>
#include <stack>
#include <set>
#include <map>
#include <string>

#define CL(a,num) memset((a),(num),sizeof(a))
#define iabs(x)  ((x) > 0 ? (x) : -(x))
#define Min(a , b) ((a) < (b) ? (a) : (b))
#define Max(a , b) ((a) > (b) ? (a) : (b))

#define ll __int64
#define inf 0x7f7f7f7f
#define MOD 100000007
#define lc l,m,rt<<1
#define rc m + 1,r,rt<<1|1
#define pi acos(-1.0)
#define test puts("<------------------->")
#define maxn 100007
#define M 10007
#define N 20007
using namespace std;
//freopen("din.txt","r",stdin);

struct Seg{
    int l,r,h;
    int mk;
    Seg(){}
    Seg(int L,int R,int H,int MK){
        l = L; r = R; h = H; mk = MK;
    }
    bool operator < (const Seg &tmp) const{
        return h < tmp.h;
    }
}seg[M];

int len[N<<2],cnt[N<<2],numseg[N<<2];
int lbd[N<<2],rbd[N<<2];

void pushup(int rt,int l,int r){
    if (cnt[rt]){
        len[rt] = r - l + 1;
        lbd[rt] = rbd[rt] = 1;//标记边界
        numseg[rt] = 2;
    }
    else if (l == r) len[rt] = lbd[rt] = rbd[rt] = numseg[rt] = 0;
    else{
        len[rt] = len[rt<<1] + len[rt<<1|1];
        numseg[rt] = numseg[rt<<1] + numseg[rt<<1|1];
        lbd[rt] = lbd[rt<<1];
        rbd[rt] = rbd[rt<<1|1];
        if (lbd[rt<<1|1] && rbd[rt<<1]) numseg[rt] -= 2;//边界重合重合
    }
}
void update(int L,int R,int sc,int l,int r,int rt){
    if (L <= l && r <= R){
        cnt[rt] += sc;
        pushup(rt,l,r);
        return ;
    }
    int m = (l + r)>>1;
    if (L <= m) update(L,R,sc,lc);
    if (m < R) update(L,R,sc,rc);
    pushup(rt,l,r);
}
int main(){
   //freopen("din.txt","r",stdin);
    int n,i;
    int x1,y1,x2,y2;
    scanf("%d",&n);
    int m = 0;
    int L = 10001,R = -10001;
    for (i = 0; i < n; ++i){
        scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
        L = min(L,x1);//求出边界
        R = max(R,x2);
        seg[m++] = Seg(x1,x2,y1,1);
        seg[m++] = Seg(x1,x2,y2,-1);
    }
    sort(seg,seg + m);
    int res = 0;
    int last = 0;
    CL(cnt,0); CL(numseg,0); CL(len,0);
    CL(lbd,0); CL(rbd,0);
    //扫描
    for (i = 0; i < m; ++i){
        if (seg[i].l < seg[i].r)
        update(seg[i].l,seg[i].r - 1,seg[i].mk,L,R - 1,1);
        res += numseg[1]*(seg[i + 1].h - seg[i].h);//竖直方向上有效长度
        res += iabs(len[1] - last);//水平方向上的长度
        last = len[1];//last记录上次的横向的有效长度
    }
    printf("%d\n",res);
    return 0;
}

 

 

 

 

posted @ 2012-09-28 21:18  E_star  阅读(327)  评论(0编辑  收藏  举报