题意:给定平面上若干矩形,求出被这些矩形覆盖过至少两次的区域的面积。
思路:扫描线模板题,不过是求面积交(覆盖两次及以上的面积),而不是区间并(覆盖一次的面积)。所以相对于求覆盖一次以上的面积,大体套路都相同,只不过pushup函数变了变:
下面两个版本,第一个是自己写的,第二个是别人写的。
先来解释一下:t[k].sum代表被覆盖大于等于一次的面积,t[k].sum2代表被覆盖大于等于两次的面积。
t[k].c代表当前区间被完全覆盖了几次,这个很重要。
三种情况:
1、若当前区间完全覆盖次数大于2,那sum 和 sum2都赋值区间长度。
2、若当前区间完全覆盖次数为1,那sum直接赋值区间长度,sum2是左右子树覆盖次数大于1的区间长度(1+1才能凑出2嘛)。
3、若当前区间完全覆盖次数为0,那sum需要看左右子树覆盖次数大于1的长度,sum2需要看左右子树覆盖次数大于2的长度(0+2才能凑出2嘛)。
当然 以上2、3条(情况1 特不特判都一样 因为它不看子树啥样) 一定一定要特判一下当前节点是否为叶结点:2时,若为叶结点,当前sum赋值区间长度,sum2为0(因为不可能再从子树找了)。3时,若为叶结点,那sum和sum2都为0。
下面是核心代码: 这段是我写的 觉得比较好理解
void pushup(int k){//换成先处理一次 后处理两次
if(t[k].c>1){//若该段被覆盖了两次
t[k].sum=y[t[k].r+1]-y[t[k].l];
t[k].sum2=y[t[k].r+1]-y[t[k].l];
}else if(t[k].c==1){//若该段全被覆盖了一次 就看两个子树各一次之和
if(t[k].l==t[k].r){//若是叶结点,那sum就赋值区间长度 sum2就是0
t[k].sum=y[t[k].r+1]-y[t[k].l];
t[k].sum2=0;
}else{
t[k].sum=y[t[k].r+1]-y[t[k].l];
t[k].sum2=t[k<<1].sum+t[k<<1|1].sum;
}
}else if(t[k].c==0){//0次
if(t[k].l==t[k].r){//若是叶子结点
t[k].sum=t[k].sum2=0;
}else{
t[k].sum=t[k<<1].sum+t[k<<1|1].sum;
t[k].sum2=t[k<<1].sum2+t[k<<1|1].sum2;
}
}
}
这个是我看的题解写的,是先处理覆盖过一次的,再处理覆盖过两次的(分割线前后),感觉交换顺序影响不大~
void pushup(int k){
//第一部分处理覆盖一次的 第二部分在第一部分的基础上处理!!!
if(t[k].c>0){
t[k].sum=y[t[k].r+1]-y[t[k].l];
}else{//该点没有被全覆盖
if(t[k].l==t[k].r) t[k].sum=0;
else t[k].sum=t[k<<1].sum+t[k<<1|1].sum;
}
//在覆盖一次的基础上处理(顺序其实无所谓
if(t[k].c>1){//若当前结点全被覆盖两次
t[k].sum2=t[k].sum;
}else{//若不是全被覆盖两次
if(t[k].l==t[k].r) t[k].sum2=0;
else if(t[k].c==1){//该段被完整覆盖1次
t[k].sum2=t[k<<1].sum+t[k<<1|1].sum;
}else{//该段被完整覆盖0次
t[k].sum2=t[k<<1].sum2+t[k<<1|1].sum2;
}
}
}
下面是AC代码:
#include<cstdio>
#include<iostream>
#include<cmath>
#include<cstring>///attention!!!!!!!!
#include<algorithm>//重写 不能套自己写的!!!
#define ll long long
#define maxn 4003
#define cl(a,b) memset(a,b,sizeof(a))
using namespace std;
int n,cnt,size,casee=0;///cnt为总边数 size为去重后y长度
double y[maxn];///y里面放去重排序后的y 然后可从里面查找离散化后对应整数(下标
struct Side{
double x,y1,y2;///y2>y1
int k;
}side[maxn];
struct node{
int l,r,c;///c来记录次数 是否有!
double sum,sum2;//sum2代表两次的面积
}t[maxn<<1];//因为本来就是乘以二
void init(){
cnt=0;
cl(y,0);
cl(side,0);
cl(t,0);
}
bool cmp(Side a,Side b){///扫描顺序排序
return a.x<b.x;
}
void pushup(int k){//换成先处理一次 后处理两次
if(t[k].c>1){//若该段被覆盖了两次
t[k].sum=y[t[k].r+1]-y[t[k].l];
t[k].sum2=y[t[k].r+1]-y[t[k].l];
}else if(t[k].c==1){//若该段全被覆盖了一次 就看两个子树各一次之和
if(t[k].l==t[k].r){
t[k].sum=y[t[k].r+1]-y[t[k].l];
t[k].sum2=0;
}else{
t[k].sum=y[t[k].r+1]-y[t[k].l];
t[k].sum2=t[k<<1].sum+t[k<<1|1].sum;
}
}else{//0次
if(t[k].l==t[k].r){//若是叶子结点
t[k].sum=t[k].sum2=0;
}else{
t[k].sum=t[k<<1].sum+t[k<<1|1].sum;
t[k].sum2=t[k<<1].sum2+t[k<<1|1].sum2;
}
}
}
void build(int k,int l,int r){
t[k].l=l,t[k].r=r;
if(l==r){
t[k].c=t[k].sum=t[k].sum2=0;
}else{
int mid=(l+r)>>1;
build(k<<1,l,mid);
build(k<<1|1,mid+1,r);
//t[k].c=t[k].sum=0;
pushup(k);//记住这里memset才对 不然容易错!
}
}
void updata(int k,int l,int r,int v){
if(l<=t[k].l&&t[k].r<=r){//若到了目的区间!
t[k].c+=v;
pushup(k);
}else{
int mid=(t[k].l+t[k].r)>>1;
if(l<=mid) updata(k<<1,l,r,v);
if(mid<r) updata(k<<1|1,l,r,v);
pushup(k);
}
}
void input(){
init();///初始化
for(int i=1;i<=n;i++){///输入所有点边信息
double x1,y1,x2,y2;
scanf("%lf%lf%lf%lf",&x1,&y1,&x2,&y2);//输入顺序刚开始有问题!!!
cnt++;
y[cnt]=y1;
side[cnt].x=x1,side[cnt].y1=y1,side[cnt].y2=y2,side[cnt].k=1;
cnt++;
y[cnt]=y2;
side[cnt].x=x2,side[cnt].y1=y1,side[cnt].y2=y2,side[cnt].k=-1;
}
}
void lsh(){
sort(y+1,y+cnt+1);
size=unique(y+1,y+cnt+1)-(y+1);
sort(side+1,side+cnt+1,cmp);
}
int id(double yy){
return lower_bound(y+1,y+size+1,yy)-y;//这里应该用size。。。否则re了!!!
}
void calculate(){///计算面积
build(1,1,size-1);
double ans=0;
for(int i=1;i<=n*2-1;i++){//对于所有的x(除最后一条边
updata(1,id(side[i].y1),id(side[i].y2)-1,side[i].k);
ans+=(t[1].sum2*(side[i+1].x-side[i].x));//不需要query!!!
}//刚开始就输出一个数 能对吗???笑死
printf("%.2lf\n",ans);
}
int main(){
int T;
cin>>T;
while(T--){
scanf("%d",&n);
input();///输入各个对角点的信息
lsh();///把y离散化 把各个对角点按x排序
calculate();///计算面积
}
return 0;
}
给定平面上若干矩形,求出被这些矩形覆盖过至少两次的区域的面积.
Input
输入数据的第一行是一个正整数T(1<=T<=100),代表测试数据的数量.每个测试数据的第一行是一个正整数N(1<=N<=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