poj 1151 Atlantis && codeforces #337 D. Vika and Segments (线段树+扫描线)(求面积并)

题目:http://poj.org/problem?id=1151 

http://codeforces.com/contest/610/problem/D

题意:告诉n个矩形的顶点坐标,求面积并。

分析:几篇比较好的介绍线段树扫描线的博客:

http://www.cnblogs.com/kane0526/archive/2013/02/26/2934214.html

http://blog.csdn.net/xingyeyongheng/article/details/8927732

http://www.cnblogs.com/anhuizhiye/p/3574724.html?utm_source=tuicool&utm_medium=referral

需要注意的是,假设扫描线是从下往上扫描,那么矩形的宽很好求,而底边长需要线段树维护(假如求几处的面积,底边还不一定连续),首先将横坐标离散化,便于将底边插入线段树。pushup的时候,当cover[rt]>0,那么sum[rt]=X[r+1]-X[l],为什么要这样写,而不是sum[rt]=X[r]-X[l]?是为了不漏掉[mid,mid+1]这段区间。所以在插入线段的时候,离散化后的右端点R=Find(s[i].xr,cnt)-1。

代码:

#include <iostream>
#include <cstdio>
#include <string.h>
#include <algorithm>
using namespace std;

typedef long long LL;
typedef unsigned long long ULL;
const LL INF = 1e9+7;
const LL MINT = ~0u>>1;

#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1

const int maxn = (1e5+7)*8;

struct seg
{
	double xl,xr,h;  //线段的左右端点的横坐标,以及线段的高度
	int d;        //标记是上底边还是下底边
	seg(){} 
	seg(double x1,double x2,double sh,int f):
		xl(x1),xr(x2),h(sh),d(f){}
	bool operator < (const seg &s)const
	{
		return h<s.h;
	}
}s[maxn];

double sum[maxn];   //区间实际长度 
int cover[maxn];  //记录覆盖的次数 
double X[maxn];  //离散x坐标 

void pushup(int l,int r,int rt)
{
	if(cover[rt]) sum[rt]=X[r+1]-X[l];
	else if(l==r)	sum[rt]=0;
	else sum[rt]=sum[rt<<1]+sum[rt<<1|1];
}
void update(int L,int R,int c,int l,int r,int rt)
{
	if(L<=l && r<=R)
	{
		cover[rt]+=c;
		pushup(l,r,rt);
		return ; 
	}
	int m=(l+r)>>1;
	if(L<=m)
		update(L,R,c,lson);
	if(R>m)
		update(L,R,c,rson);
	pushup(l,r,rt);
}
int Find(double val,int n)
{
	int down=1,mid,up=n;
	while(down<=up)
	{
		mid=(down+up)>>1;
		if(X[mid]==val)	return mid;
		else if(X[mid]>val)	up=mid-1;
		else down=mid+1;
	}
	return -1;
}
int main()
{
	int n,nCase=0;
	while(scanf("%d",&n),n)
	{
		int num=0;
		memset(sum,0,sizeof(sum));
		memset(cover,0,sizeof(cover));
		for(int i=1;i<=n;i++)
		{
			double x1,y1,x2,y2;
			scanf("%lf%lf%lf%lf",&x1,&y1,&x2,&y2);
			s[++num]=seg(x1,x2,y1,1);
			X[num]=x1;
			s[++num]=seg(x1,x2,y2,-1);
			X[num]=x2;
		}
		sort(s+1,s+num+1);
		sort(X+1,X+num+1);
		int cnt=1;
		for(int i=2;i<=num;i++)
			if(X[i]!=X[cnt])	X[++cnt]=X[i];
		double ans=0;
		for(int i=1;i<num;i++)
		{
			int L=Find(s[i].xl,cnt);
			int R=Find(s[i].xr,cnt)-1;
			update(L,R,s[i].d,1,cnt,1);
			ans+=sum[1]*(s[i+1].h-s[i].h);
		}
		printf("Test case #%d\n",++nCase);
		printf("Total explored area: %.2lf\n\n",ans);
	}
	return 0;
}


对于cf上的那题,只要将坐标转化一下就好了。注意线段树的数组要开到800 000。横坐标有2*100000个。

代码:

#include <bits/stdc++.h>
using namespace std;

typedef long long LL;
typedef unsigned long long ULL;
const LL INF = 1e9+7;
const LL MINT = ~0u>>1;

const int maxn = (1e5+7)*8;
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1

struct seg
{
	int xl,xr,h,d;
	seg()=default;
	seg(int x1,int x2,int sh,int f):
		xl(x1),xr(x2),h(sh),d(f){}
	bool operator < (const seg &s)const
	{
		return h<s.h;
	}
}s[maxn];

int sum[maxn];
int cover[maxn];
int X[maxn];

void pushup(int l,int r,int rt)
{
	if(cover[rt])	sum[rt]=X[r+1]-X[l];
	else if(l==r)	sum[rt]=0;
	else sum[rt]=sum[rt<<1]+sum[rt<<1|1]; 
}
void update(int L,int R,int c,int l,int r,int rt)
{
	if(L<=l && r<=R)
	{
		cover[rt]+=c;
		pushup(l,r,rt);
		return ;
	}
	int m=(l+r)>>1;
	if(L<=m)	update(L,R,c,lson);
	if(R>m)	    update(L,R,c,rson);
	pushup(l,r,rt);
}
int Find(int val,int n)
{
	int down=1,mid,up=n;
	while(down<=up)
	{
		mid=(down+up)>>1;
		if(X[mid]==val)	return mid;
		else if(X[mid]>val)	up=mid-1;
		else down=mid+1;
	}
	return -1;
}
void process(int &x1,int &y1,int &x2,int &y2)
{
	if(x1==x2)
	{
		if(y1>y2)
			swap(y1,y2);
	}
	else
	{
		if(x1>x2)
			swap(x1,x2);
	}
	x2++;
	y2++;
}

int main()
{
	int n,num=0;
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
	{
		int x1,y1,x2,y2;
		scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
		process(x1,y1,x2,y2);
		s[++num]=seg(x1,x2,y1,1);
		X[num]=x1;
		s[++num]=seg(x1,x2,y2,-1);
		X[num]=x2;
	}
	sort(s+1,s+num+1);
	sort(X+1,X+num+1);
	int cnt=1;
	for(int i=2;i<=num;i++)
		if(X[i]!=X[cnt])	X[++cnt]=X[i];
	LL ans=0;
	for(int i=1;i<num;i++)
	{
		int L=Find(s[i].xl,cnt);
		int R=Find(s[i].xr,cnt)-1;
		update(L,R,s[i].d,1,cnt,1);
		ans+=sum[1]*1ll*(s[i+1].h-s[i].h);
	}
	printf("%lld\n",ans);
	return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值