HDU1255 覆盖的面积 扫描线模板题

6 篇文章 0 订阅
2 篇文章 0 订阅

题意:给定平面上若干矩形,求出被这些矩形覆盖过至少两次的区域的面积。
思路:扫描线模板题,不过是求面积交(覆盖两次及以上的面积),而不是区间并(覆盖一次的面积)。所以相对于求覆盖一次以上的面积,大体套路都相同,只不过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

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值