hdu 1542 upcoj 2540 矩形面积并 线段树

  几天没AC了 。。。太爷跟我说什么有个线段树的题很简单,可以做一下。。于是被坑了。。

  那个题是upcoj 2540,求一堆矩形重叠2次部分的面积,现在我还只会线段树简单的点修改区间修改,呵呵呵一看这题我果断不会做。。百度了一下,看了太爷给的一个不错的线段树汇总http://www.notonlysuccess.com/index.php/segment-tree-complete/,算是对这种矩形面积并的类型有点了解了,最基础的是hdu 1542,求矩形面积的和,upcoj上重叠2次的是这种问题的变形。

  这种题目的方法是扫描线法。先把所有x坐标记录,排序,把一个矩形的上下两条线加入结构体,按y坐标排序。注意一般的线段树区间[L,R]就是指[L,R],这里区间[L,R]指的是[x[L],x[R]]这段区间,L是点x[L],R是点x[R],取mid的时候也是x[mid]。所谓扫描线就是假设有一根线顺着y坐标从下往上扫。


  hdu 1542

Atlantis

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 6082    Accepted Submission(s): 2661


Problem Description
There are several ancient Greek texts that contain descriptions of the fabled island Atlantis. Some of these texts even include maps of parts of the island. But unfortunately, these maps describe different regions of Atlantis. Your friend Bill has to know the total area for which maps exist. You (unwisely) volunteered to write a program that calculates this quantity.
 

Input
The input file consists of several test cases. Each test case starts with a line containing a single integer n (1<=n<=100) of available maps. The n following lines describe one map each. Each of these lines contains four numbers x1;y1;x2;y2 (0<=x1<x2<=100000;0<=y1<y2<=100000), not necessarily integers. The values (x1; y1) and (x2;y2) are the coordinates of the top-left resp. bottom-right corner of the mapped area.

The input file is terminated by a line containing a single 0. Don’t process it.
 

Output
For each test case, your program should output one section. The first line of each section must be “Test case #k”, where k is the number of the test case (starting with 1). The second one must be “Total explored area: a”, where a is the total explored area (i.e. the area of the union of all rectangles in this test case), printed exact to two digits to the right of the decimal point.

Output a blank line after each test case.
 

Sample Input
  
  
2 10 10 20 20 15 15 25 25.5 0
 

Sample Output
  
  
Test case #1 Total explored area: 180.00
 

  有两种方法,第一种方法比较慢,感觉也没有第二种方法好理解,看看就行了。这种方法对于每个y坐标的扫描线的区间就是这条线的[x1,x2],并且update的时候必须要找到线段树底。


  红色代表扫描线,update的时候ly[o]代表[L,R]这个区间上一次扫描线的y坐标,l[o]和r[o]代表x[L]和x[R],covered[o]代表这个区间目前有几条线覆盖(如果covered不为0,说明当前位置在矩形内部),初始化的时候把下面的边cover设为1,上面的边cover设为-1。这种方法几乎是需要查询到叶子节点的,而且感觉没有通用性。

#include<cstdio>
#include<algorithm>
#include<iostream>
#include<sstream>
#include<cstring>
#include<queue>
#include<map>
#include<set>
#include<stack>
#define INF 0x3f3f3f3f
#define MAXN 110
#define MAXM 110
#define MAXNODE 8*MAXN
#define eps 1e-9
#define pi 4*atan(1.0)
#define pii pair<int,int>
using namespace std;
int N,M,NX,covered[MAXNODE];
double ly[MAXNODE],l[MAXNODE],r[MAXNODE],x[2*MAXN];
struct Line{
    double x1,x2,y;
    int cover;
    bool operator < (const Line &a) const{
        return y<a.y;
    }
}line[2*MAXN];
void add_line(double x1,double y1,double x2,double y2){
    line[M].x1=x1;
    line[M].x2=x2;
    line[M].y=y1;
    line[M++].cover=1;
    line[M].x1=x1;
    line[M].x2=x2;
    line[M].y=y2;
    line[M++].cover=-1;
}
void build(int o,int L,int R){
    covered[o]=0;
    l[o]=x[L];
    r[o]=x[R];
    if(L+1>=R) return;
    int mid=(L+R)>>1;
    build(o<<1,L,mid);
    build(o<<1|1,mid,R);
}
double query(int o,int L,int R,double ql,double qr,double y,int cover){
    if(ql>=x[R]||qr<=x[L]) return 0;
    double ret=0;
    if(L+1>=R){
        if(covered[o]){
            ret+=(y-ly[o])*(r[o]-l[o]);
            covered[o]+=cover;
            ly[o]=y;
        }
        else{
            covered[o]+=cover;
            ly[o]=y;
        }
        return ret;
    }
    int mid=(L+R)/2;
    if(ql<=x[mid]) ret+=query(o<<1,L,mid,ql,qr,y,cover);
    if(qr>x[mid]) ret+=query(o<<1|1,mid,R,ql,qr,y,cover);
    return ret;
}
int main(){
    freopen("in.txt","r",stdin);
    int cas=0;
    while(scanf("%d",&N),N){
        memset(covered,0,sizeof(covered));
        M=0;
        NX=1;
        double x1,y1,x2,y2;
        for(int i=0;i<N;i++){
            scanf("%lf%lf%lf%lf",&x1,&y1,&x2,&y2);
            x[NX++]=x1;
            x[NX++]=x2;
            add_line(x1,y1,x2,y2);
        }
        sort(x+1,x+NX);
        sort(line,line+M);
        build(1,1,NX-1);
        double ans=0;
        for(int i=0;i<M;i++) ans+=query(1,1,NX-1,line[i].x1,line[i].x2,line[i].y,line[i].cover);
        printf("Test case #%d\nTotal explored area: %.2lf\n\n",++cas,ans);
    }
    return 0;
}

  推荐第二种方法,更好理解,并且可以用于这个问题的变形,更新的时候不用查找到叶子节点,效率明显高。


  这里的扫描线当作无限长,最上面的边不用扫,设sum[o]为节点o区间共有多长的距离被覆盖,那么扫描每条线的时候用sum[1]乘以下一条线的高度减去这条线的高度(因为这部分肯定是在矩形中的),covered[o]是区间o有多少条完全覆盖这个区间的线,扫描每个线的时候更新区间的covered,维护sum。

  注意我用的区间代表的是点,比如L,就代表排序后x[L]这个点,这样的话更新的时候区间分别是[L,mid],[mid,R]而不是mid+1了,这样的话树的高度可能会比以前多一层。如果用L代表排序后x[L]到x[L+1]这条线段,就还是以前的[L,mid],[mid+1,R]了。

  update之前用了二分找当前线的左右坐标分别是第几个点,其实也可以不用找,像第一种方法那样在更新的时候判断也可以。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define INF 0x3f3f3f3f
#define MAXN 210
#define MAXNODE 4*MAXN
#define pi 4*atan(1)
using namespace std;
int N,M,covered[MAXNODE];
double sum[MAXNODE],x[MAXN];
struct Line{
    double x1,x2,y;
    int cover;
    bool operator < (const Line & a) const{
        return y<a.y;
    }
}line[2*MAXN];
void add_line(double x1,double y1,double x2,double y2){
    line[M].x1=x1;
    line[M].x2=x2;
    line[M].y=y1;
    line[M++].cover=1;
    line[M].x1=x1;
    line[M].x2=x2;
    line[M].y=y2;
    line[M++].cover=-1;
}
int bsearch(double *a,int L,int R,double v){  //[L,R]
    while(L<=R){
        int mid=(L+R)/2;
        if(a[mid]==v) return mid;
        if(a[mid]>v) R=mid;
        else L=mid+1;
    }
    return -1;
}
void maintain(int o,int L,int R){
    if(covered[o]) sum[o]=x[R]-x[L];
    else if(L+1>=R) sum[o]=0;
    else sum[o]=sum[o<<1]+sum[o<<1|1];
}
void update(int o,int L,int R,int l,int r,int cover){
    if(l<=L&&r>=R){
        covered[o]+=cover;
        maintain(o,L,R);
        return;
    }
    int mid=(L+R)>>1;
    if(l<mid) update(o<<1,L,mid,l,r,cover);
    if(r>mid) update(o<<1|1,mid,R,l,r,cover);
    maintain(o,L,R);
}
int main(){
    //freopen("in.txt","r",stdin);
    int cas=0;
    while(scanf("%d",&N),N){
        int NX=1;
        M=0;
        double x1,y1,x2,y2;
        for(int i=0;i<N;i++){
            scanf("%lf%lf%lf%lf",&x1,&y1,&x2,&y2);
            x[NX++]=x1;
            x[NX++]=x2;
            add_line(x1,y1,x2,y2);
        }
        sort(x+1,x+NX);
        sort(line,line+M);
        NX=unique(x+1,x+NX)-x-1;
        memset(covered,0,sizeof(covered));
        memset(sum,0,sizeof(sum));
        double ans=0;
        for(int i=0;i<M-1;i++){
            int l=bsearch(x,1,NX,line[i].x1),r=bsearch(x,1,NX,line[i].x2);
            update(1,1,NX,l,r,line[i].cover);
            ans+=sum[1]*(line[i+1].y-line[i].y);
        }
        printf("Test case #%d\nTotal explored area: %.2lf\n\n",++cas,ans);
    }
    return 0;
}

 upc 2540

2540: Monitoring System

Time Limit: 5 Sec  Memory Limit: 128 MB
Submit: 39  Solved: 11
[ Submit][ Status][ Web Board]

Description

BCBC is a large bank. To ensure safety, they have set many cameras to monitor the total bank. Recently, they have found some problems in the monitoring system. In the current system, a camera can monitor a rectangle region. Some regions can be monitored by three or more cameras, which is waste; some regions can only be monitored by just one camera, which is not safe. So the manage intends to improve the system. Now he wants to know how many regions are monitored by just two cameras..

Input

There are multiple test cases. Each test case begins with an integer n (1<=n<=100000) standing for the number of cameras. Then n lines follow. Each line has four integers x1, y1, x2, y2 (1<x1, x2, y1, y2<10^9). The four integer descript the rectangle region one camera can monitor, (x1, y1) is the left bottom coordinate and (x2, y2) is the right up coordinate.

Output

.   Each test case output one integer that is the area of regions covered by just exactly two cameras, the result occupied one line.

Sample Input

31 1 3 32 2 4 41 2 3 4

Sample Output

2

  求覆盖2次的面积和。只要求出覆盖2次以上和3次以上的面积和相减就行。代码和上面的几乎相同,就是维护sum的时候要维护sum1,sum2,sum3,分别代表覆盖1次、2次、3次的面积和。维护sum2和sum3的函数和sum1有点不同。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define INF 0x3f3f3f3f
#define MAXN 200010
#define MAXNODE 4*MAXN
#define pi 4*atan(1)
using namespace std;
int N,M,covered[MAXNODE],sum1[MAXNODE],sum2[MAXNODE],sum3[MAXNODE],x[MAXN];
struct Line{
    int x1,x2,y,cover;
    bool operator < (const Line & a) const{
        return y<a.y;
    }
}line[2*MAXN];
void add_line(int x1,int y1,int x2,int y2){
    line[M].x1=x1;
    line[M].x2=x2;
    line[M].y=y1;
    line[M++].cover=1;
    line[M].x1=x1;
    line[M].x2=x2;
    line[M].y=y2;
    line[M++].cover=-1;
}
int bsearch(int *a,int L,int R,int v){  //[L,R]
    while(L<=R){
        int mid=(L+R)/2;
        if(a[mid]==v) return mid;
        if(a[mid]>v) R=mid;
        else L=mid+1;
    }
    return -1;
}
void maintain1(int o,int L,int R){
    if(covered[o]) sum1[o]=x[R]-x[L];
    else if(L+1>=R) sum1[o]=0;
    else sum1[o]=sum1[o<<1]+sum1[o<<1|1];
}
void maintain2(int o,int L,int R){
    if(covered[o]>=2) sum2[o]=x[R]-x[L];
    else if(covered[o]==1){
        if(L+1>=R) sum2[o]=0;
        else sum2[o]=sum1[o<<1]+sum1[o<<1|1];
    }
    else{
        if(L+1>=R) sum2[o]=0;
        else sum2[o]=sum2[o<<1]+sum2[o<<1|1];
    }
}
void maintain3(int o,int L,int R){
    if(covered[o]>=3) sum3[o]=x[R]-x[L];
    else if(covered[o]==2){
        if(L+1>=R) sum3[o]=0;
        else sum3[o]=sum1[o<<1]+sum1[o<<1|1];
    }
    else if(covered[o]==1){
        if(L+1>=R) sum3[o]=0;
        else sum3[o]=sum2[o<<1]+sum2[o<<1|1];
    }
    else{
        if(L+1>=R) sum3[o]=0;
        else sum3[o]=sum3[o<<1]+sum3[o<<1|1];
    }
}

void update(int o,int L,int R,int l,int r,int cover){
    if(l<=L&&r>=R){
        covered[o]+=cover;
        maintain1(o,L,R);
        maintain2(o,L,R);
        maintain3(o,L,R);
        return;
    }
    int mid=((L+R)>>1);
    if(l<mid) update(o<<1,L,mid,l,r,cover);
    if(r>mid) update(o<<1|1,mid,R,l,r,cover);
    maintain1(o,L,R);
    maintain2(o,L,R);
    maintain3(o,L,R);
}
int main(){
    freopen("in.txt","r",stdin);
    int cas=0;
    while(scanf("%d",&N)!=EOF){
        int NX=1,x1,y1,x2,y2;
        M=0;
        for(int i=0;i<N;i++){
            scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
            x[NX++]=x1;
            x[NX++]=x2;
            add_line(x1,y1,x2,y2);
        }
        sort(x+1,x+NX);
        sort(line,line+M);
        NX=unique(x+1,x+NX)-x-1;
        int MAX=(NX<<2);
        for(int i=0;i<=MAX+10;i++) covered[i]=sum1[i]=sum2[i]=sum3[i]=0;
        long long ans=0;
        for(int i=0;i<M-1;i++){
            int l=bsearch(x,1,NX,line[i].x1),r=bsearch(x,1,NX,line[i].x2);
            if(line[i].x1!=line[i].x2)
            update(1,1,NX,l,r,line[i].cover);
            ans+=(long long)(sum2[1]-sum3[1])*(line[i+1].y-line[i].y);
        }
        printf("%lld\n",ans);
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值