杭电1542——Atlantis(线段树+扫描线+离散化)

题目简述:输入n个矩形的左下和右上点,求出所有矩阵覆盖的最大面积。

样例输入:

2 
10 10 20 20 
15 15 25 25.5 
0

样例输出:

Test case #1 
Total explored area: 180.00

大致思路:

       1)尽管坐标的范围在0-100000;但只有100个点,故应当想到离散化。

       2)扫描线,沿y轴向上对平行x轴的线进行操作;

       3)update函数的工作是确定两条线之间的有效长度。

       4)update函数的工作方式:

从低到高,对橘红色的横线进行update操作:

引入第一条横线时,tree数组的1-3区间flag=1;

引入第二条横线时,tree数组的1-2、3-6区间flag=1,2-3区间flag=2;

引入第三条横线时,tree数组的1-2、3-4、5-6区间flag=1,2-3、4-5区间flag=2;

引入第四条横线时,因为它是出边,故tree数组2-4、5-6区间flag=1,4-5区间flag=2;

第五条,区间4-5的flag=1,其余区间flag为0;

第六条,Tree数组所有区间flag变回0。

值得注意的是,ans是在update操作前进行"+="的,也就是说,本次update后的有效横线长,是为了下一次运算,所以要在循环前先引入第一条边。

AC代码:

#include<algorithm>
#include<iostream> 
#include<cstdio>
#include<map>
#define ll long long
using namespace std;
double linex[210],ans;//x轴的值 
int n;
map<double,int>xindex;//离散化后x对应的下标 
struct node
{
	double h;//线的高度 
	double l;//左端 
	double r;//右段 
	int flag;//1为入边,-1为出边
	friend bool operator <(node a,node b)	{return a.h<b.h;}//线按高度排序 
}line[210];//存放线的数组 
struct point
{
	double key;//节点的值
	ll flag;//这个节点区间被覆盖的次数 
}tree[840]; //线段数上的节点 

void pushup(ll n)//向上更新 
{
	if(tree[n].flag==0)  //若这个区间没被覆盖
	tree[n].key=tree[n*2+1].key+tree[n*2].key; 
} 

void build(ll n, ll l, ll r)//建数的函数 
{ 
	tree[n].flag = 0; //覆盖标记置0 
	if(l+1==r){ //如果左右边界相差1,就是叶子节点了 
		tree[n].key = 0; 
		return ;
	}
	ll mid=(l+r)/2;
	build(2*n, l, mid); //建左子树 
	build(2*n+1, mid, r); //建右子树 
	pushup(n);	//向上更新 
}

void update(ll n,ll l,ll r,ll x,ll y,ll val)//对[x,y]区间更新,val=1表示覆盖一次,=-1表示取消一次覆盖 
{
	if(l+1==r)//如果是叶子节点 
	{
		tree[n].flag+=val;//修改覆盖标记 
		if(tree[n].flag) tree[n].key=linex[r]-linex[l];//如果被覆盖,修改节点的值 
		else tree[n].key=0;
		return;
	}
	if(l==x&&r==y) //如果找到当前节点 
	{
		tree[n].flag += val; //修改覆盖标记 
		if(tree[n].flag) //如果被覆盖 
		tree[n].key = linex[r]-linex[l]; //节点的值为下标对应的linex数组的值之差 
		else //如果没被覆盖 
		pushup(n); //它的值等于左右子区间的值之和
	}
	else{
		ll mid=(l+r)/2;
		if(x<mid) 
		update(2*n, l, mid, x, min(y,mid),val); //在左子树更新 
		if(y>mid)
		update(2*n+1, mid, r, max(x,mid), y,val); //在右子树更新 
		pushup(n); //向上更新 
	}	
} 

int main()
{
	int ycnt,xcnt,tcase=0; //ycnt表示线的数量,xcnt表示离散化后x值的数量
	double x1,x2,y1,y2;
	while(scanf("%d",&n)&&n)
	{
		build(1,1,n);
		ycnt=0;
		for(int i=1;i<=n;i++)
		{
			scanf("%lf%lf%lf%lf",&x1,&y1,&x2,&y2) ;
			linex[++ycnt]=x1;
			line[ycnt].l=x1;
			line[ycnt].r=x2;
			line[ycnt].h=y1;
			line[ycnt].flag=1;
			linex[++ycnt]=x2;
			line[ycnt].l=x1;
			line[ycnt].r=x2;
			line[ycnt].h=y2;
			line[ycnt].flag=-1;
		}
		sort(linex+1,linex+ycnt+1);//对x轴的值排序
		xcnt=unique(linex+1,linex+1+ycnt)-1-linex;//对x值数组去重
		for(int i=1;i<=xcnt;i++)
			xindex[linex[i]] = i; //对于x值数组,离散化 
		sort(line+1,line+ycnt+1); //对线数组由低到高排序
		ans=0;
		update(1,1,xcnt,xindex[line[1].l],xindex[line[1].r],line[1].flag);//放入第一条线 
		for(int i=2;i<=ycnt;i++)
		{
			ans+=tree[1].key*(line[i].h-line[i-1].h);
			update(1,1,xcnt,xindex[line[i].l],xindex[line[i].r],line[i].flag);//放入线i 
		}
		printf("Test case #%d\n",++tcase);
		printf("Total explored area: %.2lf\n\n",ans); //输出答案
	}
	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值