hdu1542 Atlantis(线段树-扫描线求面积并)

题目

给定n(n<=100)个矩形,

每个矩形x1,y1,x2,y2四个参数,代表左下角的点(x1,y1)和右上角的点(x2,y2)

求所有矩形的面积并

思路来源

https://www.cnblogs.com/KonjakJuruo/p/6024266.html(扫描线具体流程)

https://blog.csdn.net/stay_accept/article/details/50503332(含代码注释,较为详细)

题解

注意矩形n为100的话,至少200条边,

这样建线段树的话就需要开800的数组,不过还是开2000比较稳...

每次计算当前线段对当前不封顶的线段的面积的贡献,最后求和即可

心得

嚷嚷着扫描线一年了,终于把扫描线入门学会了……

现在觉得,打游戏对敲代码真的有帮助鸭……

晚上自闭3h看懂了扫描线的线段和点的下标处理不同部分和扫线的部分……

估计代码可能要敲1h,然而打了一波游戏之后20min就敲出来了……

劳逸结合鸭QAQ

代码

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=2005;
int cas,n,now;
double ans;
double x[maxn];
double x1,y1,x2,y2;
struct node
{
	int l,r;
	double len;//每个节点都维护一个线段长度
	int cnt;//当前有cnt条重叠的下线段 
}e[maxn];
struct edge
{
	double l,r,h;//当前线段的左界,右界,高度 
	int op;//矩形op==1是下线段,-1是上线段 
	edge(){}
	edge(double ll,double rr,double hh,int o):l(ll),r(rr),h(hh),op(o){}
	friend bool operator<(edge a,edge b){return a.h<b.h;}
}g[maxn];
void build(int p,int l,int r)
{
	e[p].l=l;e[p].r=r;
	e[p].len=e[p].cnt=0;
	if(l==r)return;
	int mid=(l+r)/2;
	build(p<<1,l,mid);
	build(p<<1|1,mid+1,r); 
}
void pushup(int p)
{
	if(e[p].cnt>0)e[p].len=x[e[p].r+1]-x[e[p].l];//[l,r)的线段由[l,r-1]表示,但长度仍为[l,r]的长度 
	else if(e[p].l==e[p].r)e[p].len=0;
	else e[p].len=e[p<<1].len+e[p<<1|1].len;//左右子的贡献之和 
}
void update(int p,int ql,int qr,int op)
{
	if(ql<=e[p].l&&e[p].r<=qr)
	{
		e[p].cnt+=op;
		pushup(p);
		return;
	}
	int mid=(e[p].l+e[p].r)/2;
	if(ql<=mid)update(p<<1,ql,qr,op);
	if(qr>mid)update(p<<1|1,ql,qr,op);
	pushup(p);
}
int main()
{
	while(~scanf("%d",&n)&&n)
	{
		ans=0;cas++;
		for(int i=1;i<=n;++i)//1到2*n
		{
			scanf("%lf%lf%lf%lf",&x1,&y1,&x2,&y2);
			x[2*i-1]=x1;x[2*i]=x2;
			g[2*i-1]=edge(x1,x2,y1,1);
			g[2*i]=edge(x1,x2,y2,-1); 
		}
		sort(x+1,x+2*n+1);
		now=1;
		for(int i=2;i<=2*n;++i)//强制去重x数组 
		if(x[i]!=x[now])x[++now]=x[i];
		build(1,1,now);
		sort(g+1,g+2*n+1);
		for(int i=1;i<=2*n-1;++i)
		{
			int l=lower_bound(x+1,x+now+1,g[i].l)-x;
			int r=lower_bound(x+1,x+now+1,g[i].r)-x;
			//[l,l+1)代表线段l,边权线段比点少一个 3个点2条边 
			//[l,r)内有线段r-l条,故用[l,r-1]来代替
			//每个线段对应一个标号 
			update(1,l,r-1,g[i].op); 
			ans+=(g[i+1].h-g[i].h)*e[1].len;
		} 
		printf("Test case #%d\nTotal explored area: %.2lf\n\n",cas,ans);
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/Code92007/article/details/89740962
今日推荐