hdu 1255 覆盖的面积(线段树 面积 交) (待整理)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1255

Description

给定平面上若干矩形,求出被这些矩形覆盖过至少两次的区域的面积.



hdu 1255 覆盖的面积(线段树 面积 交) (待整理)

 

Input

输入数据的第一行是一个正整数T(1<=T<=100),代表测试数据的数量.每个测试数据的第一行是一个正整数N(1<=N&lt;=1000),代表矩形的数量,然后是N行数据,每一行包含四个浮点数,代表平面上的一个矩形的左上角坐标和右下角坐标,矩形的上下边和X轴平行,左右边和Y轴平行.坐标的范围从0到100000.



注意:本题的输入数据较多,推荐使用scanf读入数据.
 

Output

对于每组测试数据,请计算出被这些矩形覆盖过至少两次的区域的面积.结果保留两位小数.
 

Sample Input

2
5
1 1 4 2
1 3 3 7
2 1.5 5 4.5
3.5 1.25 7.5 4
6 3 10 7
3
0 0 1 1
1 0 2 1
2 0 3 1
 

Sample Output

7.63
0.00
 



点更新:
#include <iostream>
#include <algorithm>
#include <stdio.h>
using namespace std;
const int maxn=1100;
struct LINE
{
double x, y_down, y_up;
int flag;
bool operator<(const LINE &a)const ///按照x从小到大的顺序排序
{
return x<a.x;
}
}line[2*maxn]; struct TREE
{
double x,y_down, y_up;
int cover; ///用以表示加进线段树中的线段次数
bool flag; ///标记叶子节点
}tree[maxn*8]; double y[2*maxn]; void build(int i, int l, int r) ///当前节点下标,l , r 线段树建立左右线数组下标
{
tree[i].x = -1; //-1表示该区间已经没有线段
tree[i].cover = 0; //表示该区间上有多少条线段;左边线段加进去则++,右边线段加进去则--
tree[i].y_down = y[l];
tree[i].y_up = y[r];
tree[i].flag = false;
if(l+1==r)
{
tree[i].flag = true; //flag==true表示达到了叶子节点
return;
}
int mid=(l+r)>>1;
build(2*i, l, mid);
build(2*i+1, mid, r);
} double insert(int i, double x, double l, double r, int flag) //flag表示为左边还是右边
{
if ( r<=tree[i].y_down || l>=tree[i].y_up ) return 0;
if (tree[i].flag) /// 叶子节点
{
if (tree[i].cover > 1) /// 该区域的面积存在,且未经计算
{
double temp_x = tree[i].x;
double ans=( x-temp_x )*(tree[i].y_up - tree[i].y_down);
tree[i].cover += flag;
tree[i].x = x; //定位上一次的x
return ans;
}
else ///虽然是叶子节点,但是需要更新当前的线段覆盖标记
{
tree[i].cover += flag;
tree[i].x = x; ///更新最新x
return 0;
}
}
return insert(2*i, x, l, r, flag)+insert(2*i+1, x, l, r, flag); ///不是叶子节点就往下递归
} int main( )
{
// freopen("d:\\in.txt","r",stdin);
int n,index;
double x1, y1, x2, y2;
int T;
scanf("%d",&T);
while(T--)
{
scanf("%d",&n) ;
index = 1;
for (int i=1; i<=n; i++)
{
scanf("%lf%lf%lf%lf", &x1, &y1, &x2, &y2);
y[index] = y1;
line[index].x = x1;
line[index].y_down = y1;
line[index].y_up = y2;
line[index++].flag = 1; //1表示左边 y[index] = y2;
line[index].x = x2;
line[index].y_down = y1;
line[index].y_up = y2;
line[index++].flag = -1; //-1表示右边
}
sort(&y[1], &y[index]); //把所有的纵坐标按从小到大排序,把1写成了0,WA一次
sort(&line[1], &line[index]);
build(1, 1, index-1);
double ans=0;
for (int i=1;i<index; i++) ///将线line从左向右遍历
ans+=insert(1, line[i].x, line[i].y_down, line[i].y_up, line[i].flag);
printf("%.2f\n", ans);
}
return 0;
}

区间更新:

/*
HDU 1255 覆盖的面积
求矩形交的面积(线段树+离散化)
给定一些矩形
被这些矩形覆盖过至少两次的区域的面积
*/
#include<stdio.h>
#include<iostream>
#include<algorithm>
using namespace std;
#define MAXN 2010
struct Node
{
int l,r; ///线段树的左右整点
int c; ///c用来记录重叠情况
double lf,rf; ///rf,lf分别是对应的左右真实的浮点数端点
double cnt,more;///cnt是值被覆盖一次以上的长度,more值被覆盖两次以上的长度
}segTree[MAXN*3];
struct Line
{
double x,y_down,y_up;
int flag;
}line[MAXN];
///用来记录重叠情况,可以根据这个来计算,nod节点中的c bool cmp(Line a,Line b)//sort排序的函数
{
return a.x < b.x;
} double y[MAXN];//记录y坐标的数组
void Build(int t,int l,int r)//构造线段树
{
segTree[t].l=l;
segTree[t].r=r;
segTree[t].cnt=segTree[t].c=0;
segTree[t].lf=y[l];
segTree[t].rf=y[r];
if(l+1==r)
return;
int mid=(l+r)>>1;
Build(t<<1,l,mid);
Build(t<<1|1,mid,r);//递归构造
}
void calen(int t)//计算长度
{
if(segTree[t].c>=2) ///该区间被覆盖两次或以上
{
segTree[t].more=segTree[t].cnt=segTree[t].rf-segTree[t].lf;
return;
}
else if(segTree[t].c==1) ///该区间被覆盖一次
{
segTree[t].cnt = segTree[t].rf-segTree[t].lf;
if(segTree[t].l+1==segTree[t].r)
segTree[t].more=0;
else
segTree[t].more = segTree[t<<1].cnt + segTree[t<<1|1].cnt;
}
else ///该区间
{
if(segTree[t].l+1==segTree[t].r) ///子节点
segTree[t].more=segTree[t].cnt=0;
else ///非子节点
{
segTree[t].cnt=segTree[t<<1].cnt+segTree[ t<<1|1 ].cnt;
segTree[t].more=segTree[t<<1].more+segTree[ t<<1|1 ].more;
}
}
}
void update(int t,Line line)//加入线段e,后更新线段树
{
if( line.y_down==segTree[t].lf && line.y_up==segTree[t].rf ) ///恰好是当前的区间
segTree[t].c += line.flag;
else{
if(line.y_up<=segTree[t<<1].rf) ///需要更新的区间在当前节点的左孩子节点中
update(t<<1,line);
else if(line.y_down>=segTree[t<<1|1].lf) ///需要更新的区间在当前节点的右孩子节点中
update(t<<1|1,line);
else ///横跨左右两个孩子节点
{
Line tmp=line;
tmp.y_up=segTree[t<<1].rf;
update(t<<1,tmp);
tmp=line;
tmp.y_down=segTree[t<<1].rf;
update(t<<1|1,tmp);
}
}
calen(t);
}
int main()
{
int i,n,t,T;
double x1,y1,x2,y2;
scanf("%d",&T);
while(T--)
{
scanf("%d",&n);
t=1;
for(i=1;i<=n;i++)
{
scanf("%lf%lf%lf%lf",&x1,&y1,&x2,&y2);
line[t].x=x1;
line[t].y_down=y1;
line[t].y_up=y2;
line[t].flag=1;
y[t++]=y1; line[t].x=x2;
line[t].y_down=y1;
line[t].y_up=y2;
line[t].flag=-1;
y[t++]=y2;
}
sort(line+1,line+t,cmp);
sort(y+1,y+t);
Build(1,1,t-1);
double res=0;
for(i=1;i<t;i++)
{
update(1,line[i]);
res += segTree[1].more*(line[i+1].x-line[i].x);
}
printf("%.2lf\n",res);
}
return 0;
}
上一篇:HDU 1166 敌兵布阵 (数状数组,或线段树)


下一篇:这次是C#中的接口