light oj 1120 扫描线

本文介绍了一种通过线段树实现多个矩形面积并计算的方法。利用横坐标的离散化处理,结合线段树数据结构,实现了高效的矩形面积并计算。适用于处理大量矩形重叠情况。


Time Limit: 2000MSMemory Limit: 65536KB64bit IO Format: %lld & %llu

Submit Status uDebug

Description

Given some axis parallel rectangles, you have to find the union of their area. For example, see the shaded regions in the picture. Each rectangle will be denoted by four integers. They are x1, y1, x2, y2 where (x1, y1) denotes the lower left corner and (x2, y2) denotes the upper right corner.

For the picture above, there are three rectangles. For the yellow rectangle the co-ordinates are (0, 2) and (3, 6). For the blue rectangle the co-ordinates are (1, 3) and (6, 7). For the green rectangle the co-ordinates are (2, 1) and (5, 4). So, the union area is (the shaded region) 31 square units.

Input

Input starts with an integer T (≤ 13), denoting the number of test cases.

Each case starts with a line containing an integer n (1 ≤ n ≤ 30000). Each of the next n lines will contain four integers x1, y1, x2, y2 (0 ≤ x1, y1, x2, y2 ≤ 109, x1 < x2, y1 < y2) denoting a rectangle.

Output

For each case, print the case number and the union area.

Sample Input

2

3

0 2 3 6

1 3 6 7

2 1 5 4

2

0 0 4 4

1 1 2 5

Sample Output

Case 1: 31

Case 2: 17

Source

Problem Setter: Jane Alam Jan
Problem descriptions:
System Crawler 2016-08-08
Initialization.
Mo


模板


/*
1.保存矩形的上下边界,并且重要的,记录他们是属于上还是下,然后按高度升序排序
2.保存竖线坐标,并且去重,是为了离散化
3.以保存的上下边界数组去更新
*/
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define INF 0x3f3f3f3
#define MAX 110
#define LCH(i) ((i)<<1)
#define RCH(i) ((i)<<1 | 1)


struct segment //保存矩形上下边界
{
  double l,r,h; //左右横坐标,纵坐标
  int f; //-1为下边界,1为上边界
}ss[2*MAX];
struct node //线段树节点
{
  int l,r;
  int cnt; //该节点被覆盖的情况
  double len; //该区间被覆盖的总长度
  int mid()
  { return (l+r)>>1; }
}tt[2*MAX*4];
double pos[2*MAX];
int nums;


int cmp(struct segment a ,struct segment b)
{
  return a.h<b.h;
}


void build(int a, int b ,int rt)
{
 tt[rt].l=a; tt[rt].r=b; tt[rt].cnt=0; tt[rt].len=0;
 if(a==b) return ;
 int mid=tt[rt].mid();
 build(a,mid,LCH(rt));
 build(mid+1,b,RCH(rt));
}


int binary(double key ,int low, int high)
{
   while(low<=high)
   {
      int mid=(low+high)>>1;
      if(pos[mid] == key) return mid;
      else if(key < pos[mid]) high=mid-1;
      else                    low=mid+1;
   }
   return -1;
}




void get_len(int rt)
{
   if(tt[rt].cnt) //非0,已经被整段覆盖
      tt[rt].len = pos[tt[rt].r+1] - pos[tt[rt].l];
   else if(tt[rt].l == tt[rt].r) //已经不是一条线段
      tt[rt].len = 0;
   else //是一条线段但是又没有整段覆盖,那么只能从左右孩子的信息中获取
      tt[rt].len = tt[LCH(rt)].len + tt[RCH(rt)].len ;
}


void updata(int a, int b ,int val ,int rt)
{
   if(tt[rt].l==a && tt[rt].r==b) //目标区间
   {
      tt[rt].cnt += val; //更新这个区间被覆盖的情况
      get_len(rt);  //更新这个区间被覆盖的总长度
      return ;
   }
   int mid=tt[rt].mid();
   if(b<=mid) //只访问左孩子
      updata(a,b,val,LCH(rt));
   else if(a>mid) //只访问有孩子
      updata(a,b,val,RCH(rt));
   else //左右都要访问
   {
      updata(a,mid,val,LCH(rt));
      updata(mid+1,b,val,RCH(rt));
   }
   get_len(rt); //计算该区间被覆盖的总长度
}


int main()
{
  int Case=0;
  int n;
  while(scanf("%d",&n)!=EOF && n)
  {
    nums=0;
    for(int i=0; i<n; i++)
    {
      double x1,y1,x2,y2;
      scanf("%lf%lf%lf%lf",&x1,&y1,&x2,&y2);
      ss[nums].l=x1;  ss[nums].r=x2; ss[nums].h=y1; ss[nums].f=1;
      //记录上边界的信息
      ss[nums+1].l=x1; ss[nums+1].r=x2; ss[nums+1].h=y2; ss[nums+1].f=-1;
      //记录下边界的信息
      pos[nums]=x1; pos[nums+1]=x2;
      //记录横坐标
      nums += 2;


    }


    sort(ss,ss+nums,cmp); //横线按纵坐标升序排序
    sort(pos,pos+nums); //横坐标升序排序
    //for(int i=0; i<nums; i++) printf("%.2lf %.2lf  %.2lf\n",ss[i].l,ss[i].r,ss[i].h);
    int m=1;
    for(int i=1; i<nums; i++)
      if(pos[i]!=pos[i-1]) //去重
        pos[m++]=pos[i];


    build(0,m-1,1);  //离散化后的区间就是[0,m-1],以此建树
    double ans=0;
    for(int i=0; i<nums; i++) //拿出每条横线并且更新
    {
       int l=binary(ss[i].l,0,m-1);
       int r=binary(ss[i].r,0,m-1)-1;
       updata(l,r,ss[i].f,1); //用这条线段去更新
       ans += (ss[i+1].h-ss[i].h)*tt[1].len;
       //printf("%.2lf\n",ans);
    }
    printf("Test case #%d\n",++Case);
    printf("Total explored area: %.2f\n\n",ans);
  }
  return 0;
}




#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
using namespace std;
const int N = 300100;
struct node1
{
    int l, r, h, cnt;
}p[N<<2];
struct node2
{
    int l, r, h, cnt;
    long long len;
}tree[N<<2];
int x[N<<2];
int cmp(node1 a,node1 b)
{
    return a.h<b.h;
}
void build(int l,int r,int rt);
int binary(int z,int a,int b);
void update(int L,int R,int k,int l,int r,int rt);
void pushup(int rt);


int main()
{
    int t, ncase=1;
    scanf("%d", &t);
    while(t--)
    {
        int n;
        scanf("%d", &n);
        int k=0;
        for(int i=0;i<n;i++)
        {
            int x1, y1, x2, y2;
            scanf("%d %d %d %d",&x1, &y1, &x2, &y2);
            p[k].l=x1,p[k].r=x2,p[k].h=y1, p[k].cnt=1;
            p[k+1].l=x1,p[k+1].r=x2,p[k+1].h=y2, p[k+1].cnt=-1;
            x[k]=x1, x[k+1]=x2;
            k+=2;
        }
        sort(p,p+k,cmp);
        sort(x,x+k);
        int m=1;
        for(int i=1;i<k;i++)
        {
            if(x[i]!=x[i-1])
            {
                x[m++]=x[i];
            }
        }
        build(0,m-1,1);
        long long sum=0;
        for(int i=0;i<k-1;i++)
        {
            int l=binary(p[i].l,0,m-1);
            int r=binary(p[i].r,0,m-1)-1;
            update(l,r,p[i].cnt,0,m-1,1);
            sum+=(p[i+1].h-p[i].h)*tree[1].len;
        }
        printf("Case %d: %lld\n",ncase++,sum);
    }
    return 0;
}


void build(int l,int r,int rt)
{
    tree[rt].l=l,tree[rt].r=r,tree[rt].len=0,tree[rt].cnt=0;
    if(l==r)
    {
        return ;
    }
    int mid=(l+r)/2;
    build(lson);
    build(rson);
    return ;
}


int binary(int z,int a,int b)
{
    int l=a, r=b, mid;
    while(l<=r)
    {
        mid=(l+r)/2;
        if(x[mid]==z)
        {
            return mid;
        }
        else if(x[mid]<z)
        {
            l=mid+1;
        }
        else
        {
            r=mid-1;
        }
    }
    return -1;
}


void update(int L,int R,int k,int l,int r,int rt)
{
    if(tree[rt].l==L&&tree[rt].r==R)
    {
        tree[rt].cnt+=k;
        pushup(rt);
        return ;
    }
    int mid=(l+r)/2;
    if(R<=mid)
    {
        update(L,R,k,lson);
    }
    else if(L>mid)
    {
        update(L,R,k,rson);
    }
    else
    {
        update(L,mid,k,lson);
        update(mid+1,R,k,rson);
    }
    pushup(rt);
}


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;
    }
    return ;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值