1. 程式人生 > >poj1151 Atlantis(線段樹+離散化+掃描線)

poj1151 Atlantis(線段樹+離散化+掃描線)

題意:給出一堆座標,問最後構成的面積有多少(重複的面積只能算一次)

思路:首先,這道題的資料量完全可以暴力過的,但是下面這麼做只是想練練線段樹和離散化的結合。給出的座標不是整數,所以可以這麼做。把各個x,從小到大掃描,然後對所有y值進行離散化,給其一個整數標號,這樣y就可以用線段樹進行維護了。然後沒掃描一條x縱線就決定是從線段樹中去除還是增加那一條線段,然後算面積。

#include<cstdio> 
#include<cstring>
#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
struct node1
{
	double x,y1,y2;
	int fg;
	node1(double x,double y1,double y2,int fg):x(x),y1(y1),y2(y2),fg(fg){}
};
struct node2
{
	double l,r,sum;
	int num;
}s[100000];
vector<node1> s1;
int n;
double y[1000];
bool cmp(node1 a,node1 b)
{
	return a.x<b.x;
}
void make_tree(int root,int l,int r)
{
	//cout<<root<<" "<<l<<" "<<r<<endl;
	s[root].l=y[l];
	s[root].r=y[r];
	s[root].sum=0;
	s[root].num=0;
	if(r-l<=1) return;
	int mid=(l+r)/2;
	make_tree(root*2,l,mid);
	make_tree(root*2+1,mid,r);
}
void push_up(int root)
{
    if( s[root].num>0 )
        s[root].sum = s[root].r - s[root].l ;
    else
        s[root].sum = s[2*root].sum + s[2*root+1].sum ;
}

void update(int root,int fg,double l,double r)
{
	if(s[root].l==l&&s[root].r==r)
	{
		s[root].num+=fg;
		push_up(root);
		return;
	}
	if(s[root*2].r>l)
	{
		update(root*2,fg,l,min(r,s[root*2].r));
	}
	if(s[root*2+1].l<r)
	{
		update(2*root+1,fg,max(s[root*2+1].l,l),r);
	}
	push_up(root);
	
}
int main()
{
	//freopen("t.txt","r",stdin);
	double x1,y1,x2,y2;
	int cnt=1;
	while(scanf("%d",&n)!=EOF)
	{
		if(n==0) break;
		memset(s,0,sizeof(s));
		s1.clear();
		for(int i=0;i<n;i++)
		{
			scanf("%lf%lf%lf%lf",&x1,&y1,&x2,&y2);
			s1.push_back(node1(x1,y1,y2,1));
			s1.push_back(node1(x2,y1,y2,-1));
			y[2*i+1]=y1;
			y[2*i+2]=y2;
		}
		sort(s1.begin(),s1.end(),cmp);
		sort(y+1,y+1+2*n);
		make_tree(1,1,2*n);
		update(1,s1[0].fg,s1[0].y1,s1[0].y2);
		double sum=0;
		for(int i=1;i<2*n;i++)
		{
			sum+=(s1[i].x-s1[i-1].x)*s[1].sum;
			update(1,s1[i].fg,s1[i].y1,s1[i].y2);
		}
		printf("Test case #%d\nTotal explored area: %.2lf\n\n",cnt++,sum);
	}
	return 0;
}