亚特兰蒂斯--扫描线

题意:

输入格式

输入包含多组测试用例。

对于每组测试用例,第一行包含整数 n,表示总的地图数量。

接下来 n行,描绘了每张地图,每行包含四个数字 x1,y1,x2,y2(不一定是整数),(x1,y1)和 (x2,y2)分别是地图的左上角位置和右下角位置。

注意,坐标轴 x 轴从上向下延伸,y 轴从左向右延伸。

当输入用例 n=0时,表示输入终止,该用例无需处理。

输出格式

每组测试用例输出两行。

第一行输出 Test case #k,其中 kk 是测试用例的编号,从 1 开始。

第二行输出 Total explored area: a,其中 a 是总地图面积(即此测试用例中所有矩形的面积并,注意如果一片区域被多个地图包含,则在计算总面积时只计算一次),精确到小数点后两位数。

输入样例:

2
10 10 20 20
15 15 25 25.5
0

输出样例:

Test case #1
Total explored area: 180.00 

代码:

在y方向上建立线段树维护,扫到矩形左边的边时对应区间加1,扫到右边的边对应区间时-1。将所有的y坐标保存到一个vector中,然后离散化,在线段树中每个点都代表一个线段,比如1代表[y1,y2],2代表[y2,y3],[1,4]代表[y1,y5]都被覆盖了,n个点对应n-1个线段,建树时建立build(1,0,n-2)或build(1,1,n-1)

扫描线每次查询都是查询整个区间被覆盖的长度(tr[1].sum),故查询操作不需要pushdown

进行修改时,当一个结点被覆盖次数大于1,该节点对应区间被覆盖的长度就是它所对应的长度,当它为覆盖次数为0,该节点对应区间被覆盖的长度为它左孩子被覆盖的长度加右孩子被覆盖的长度,如果是叶子节点的覆盖次数为0,它对应的被覆盖的长度就是0。一个结点的覆盖次数加1,后面一定对应着减1,减1之前也一定有加1,有了前面的操作和限制,修改操作也是不需要pushdown的。

#include <bits/stdc++.h>

using namespace std;

const int N=1e5+10;
int n; 
typedef struct Segment{
	double x;
	double y1,y2;
	int k;
}Segment;
Segment seg[N*2];
typedef struct Node{
	int l,r; 
	int cnt;//当前区间对象对应区间被覆盖的次数 
	double sum;//当前区间对象对应区间被覆盖的长度 
}Node;
Node tr[N*4];
vector<double> vc;
bool cmp(Segment a,Segment b){
	return a.x<b.x;
}
void init(){
	vc.clear();
}
int find(double x){
	return lower_bound(vc.begin(),vc.end(),x)-vc.begin();
}
void build(int u,int l,int r){
	tr[u].l=l,tr[u].r=r;
	tr[u].sum=0,tr[u].cnt=0;
	if(l==r){
		return ;
	}
	int mid=(l+r)>>1;
	build(u<<1,l,mid);
	build(u<<1|1,mid+1,r);
}
void update(int u){
	if(tr[u].cnt>0)
		tr[u].sum=vc[tr[u].r+1]-vc[tr[u].l];
	else if(tr[u].l==tr[u].r)//叶子节点 
		tr[u].sum=0;
	else
		tr[u].sum=tr[u<<1].sum+tr[u<<1|1].sum;
}
void modify(int u,int l,int r,int k){
	if(tr[u].l>=l&&tr[u].r<=r){
		tr[u].cnt+=k;
		update(u);
		return ;
	}
	int mid=(tr[u].l+tr[u].r)>>1;
	if(l<=mid)
		modify(u<<1,l,r,k);
	if(r>mid)
		modify(u<<1|1,l,r,k);
	update(u);
}
double query(){
	return tr[1].sum;
}

int main(){
	double x1,y1,x2,y2;
	int tc=0;
	while(1){
		tc++;
		scanf("%d",&n);
		if(n==0)
			break;
		init();
		int j=0;
		for(int i=1;i<=n;i++){
			scanf("%lf%lf%lf%lf",&x1,&y1,&x2,&y2);
			seg[++j]={x1,y1,y2,1};
			seg[++j]={x2,y1,y2,-1};
			vc.push_back(y1);
			vc.push_back(y2);
		}
		sort(seg+1,seg+j+1,cmp);
		sort(vc.begin(),vc.end());
		vc.erase(unique(vc.begin(),vc.end()),vc.end());
		int num=vc.size()-1;
		build(1,0,num-1);
		double nx,ny,ans=0;
		int l,r;
		for(int i=1;i<=2*n;i++){
			if(i!=1)
				nx=seg[i].x-seg[i-1].x;
			else 
				nx=0;
			ny=query();
			ans+=nx*ny;
			l=find(seg[i].y1),r=find(seg[i].y2);
			modify(1,l,r-1,seg[i].k);
		}
		printf("Test case #%d\n",tc);
		printf("Total explored area: %.2lf\n\n",ans);
	}
	return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值