POJ 3832 Posters

题目大意:

题目链接

注释代码:

/*                                                
 * Problem ID : POJ 3832 Posters
 * Author     : Lirx.t.Una                                                
 * Language   : G++                               
 * Run Time   : 500 ms                                                
 * Run Memory : 8500 KB                                                
*/ 

#pragma G++ optimize("O2")

#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>

//海报的最大数量
#define	MAXPOSN		50000
//横坐标的最大值
#define	MAXCORD		50000

//线段树的根结点指针
#define	ROOT		1

//底边bottom为1,定边top为-1
#define	BTOM		1
#define	TOP			-1

//求树tree的左右子树(都是指针值)
#define	LFT(TREE)	( (TREE) << 1 )
#define	RHT(TREE) 	( LFT(TREE) | 1 )

using namespace std;

typedef	__int64		llg;//最后面积可能会超出int值

struct	Scan {//扫描线,和x轴平行

	int		x1, x2;//左右端点
	int		y;//高度
	char	v;//类型,也为值value,即botm还是top

	bool
	operator<(const Scan &oth)
	const {//对扫描线按照从低到高排序,尽量将底边排在前面
	
		if ( y == oth.y ) return v > oth.v;
		return y < oth.y;
	}
};

//以下两个数组构成虚线段树
//共同维护一个线段树的两个域
//由于只取每个结点所代表线段的长度因此不需要记录
  //结点所代表的线段的左右区间端点
int		val[MAXPOSN * 4];//每个结点的重叠值
int		len[MAXPOSN * 4];//每个结点的长度

Scan	scn[MAXPOSN * 8];//扫描线,每个回字都分为上下左右四块
//因此一个回字最多有8条横向扫描线
int		m;//扫描线的条数

int		max_cord, min_cord;//找出所有扫描线中的最左端点和最右端点
//以这两个值作为根结点线段的左右端点,可以有效降低线段树的规模,并
//降低二分次数(即时间复杂度)

void
add( int x1, int x2, int y, int v ) {//添加扫描线

	scn[m].x1 = x1;
	scn[m].x2 = x2;
	scn[m].y  = y;
	scn[m].v  = (char)v;

	m++;
}

void
chg_mx( int x1, int x2 ) {//对max_cord, min_cord进行跟新

	if ( x1 < min_cord ) min_cord = x1;
	if ( x2 > max_cord ) max_cord = x2;
}

void
cal_len( int tree, int lft, int rht ) {//calculate length
	//计算tree结点所代表线段的长度
	//传统算法

	//lft和rht为tree结点所代表的线段的区间

	if ( val[tree] ) len[tree] = rht - lft;
	else
		if ( 1 + lft == rht ) len[tree] = 0;
		else len[tree] = len[ LFT(tree) ] + len[ RHT(tree) ];
}

void
update( int tree, int lft, int rht, int al, int ar, int av ) {//传统的线段树update
	//只不过结点的区间由传参lft和rht指定
	//而跟新区间为adding lft和adding rht,即al和ar
	//跟新进来的扫描线的类型为adding value,即av

	if ( lft == al && ar == rht ) {
		
		val[tree] += av;
		cal_len( tree, lft, rht );

		return ;
	}

	int		mid;

	mid = ( lft + rht ) >> 1;

	if ( ar <= mid ) update( LFT(tree), lft, mid, al, ar, av );
	else if ( al >= mid ) update( RHT(tree), mid, rht, al, ar, av );
	else {
	
		update( LFT(tree), lft, mid, al, mid, av );
		update( RHT(tree), mid, rht, mid, ar, av );
	}
	cal_len( tree, lft, rht );
}

int
main() {

	int		n;//回字的个数

	int		x1, y1;//外左下角
	int		x2, y2;//外右上角
	int		x3, y3;//内左下角
	int		x4, y4;//内右上角
	
	int		i;//计数变量

	llg		ans;//最终阴影部分面积

	while ( scanf("%d", &n), n ) {
	
		//初始化
		m = 0;
		memset(val, 0, sizeof(val));
		memset(len, 0, sizeof(len));

		max_cord = -1;
		min_cord = MAXCORD + 1;

		while ( n-- ) {
		
			scanf("%d%d%d%d%d%d%d%d", &x1, &y1, &x2, &y2, &x3, &y3, &x4, &y4);

			if ( x1 != x3 ) {//空心是否和左边重合
			
				add( x1, x3, y1, BTOM );
				add( x1, x3, y2, TOP );
				chg_mx( x1, x3 );
			}
			if ( x4 != x2 ) {//空心是否和右边重合
			
				add( x4, x2, y1, BTOM );
				add( x4, x2, y2, TOP );
				chg_mx( x4, x2 );
			}
			if ( y1 != y3 ) {//空心是否和下边重合
			
				add( x3, x4, y1, BTOM );
				add( x3, x4, y3, TOP );
			}
			if ( y4 != y2 ) {//空心是否和上边重合
			
				add( x3, x4, y4, BTOM );
				add( x3, x4, y2, TOP );
			}
			chg_mx( x3, x4 );//对于上下两种情况只要计算一次即可
		}
		sort(scn, scn + m);//从低到高排序

		ans = 0;
		i   = 0;
		while ( i < m ) {
		
			do {

				update( ROOT, min_cord, max_cord, scn[i].x1, scn[i].x2, scn[i].v );
				i++;
			} while ( i < m && scn[i].y == scn[i - 1].y );//先将同一高度的扫描线update

			if ( i < m )//累加一次面积
				ans += (llg)len[ROOT] * (llg)( scn[i].y - scn[i - 1].y );
		}

		printf("%I64d\n", ans);
	}

	return 0;
}

无注释代码:

#pragma G++ optimize("O2")

#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>

#define	MAXPOSN		50000
#define	MAXCORD		50000

#define	ROOT		1

#define	BTOM		1
#define	TOP			-1

#define	LFT(TREE)	( (TREE) << 1 )
#define	RHT(TREE) 	( LFT(TREE) | 1 )

using namespace std;

typedef	__int64		llg;

struct	Scan {

	int		x1, x2;
	int		y;
	char	v;

	bool
	operator<(const Scan &oth)
	const {
	
		if ( y == oth.y ) return v > oth.v;
		return y < oth.y;
	}
};

int		val[MAXPOSN * 4];
int		len[MAXPOSN * 4];
Scan	scn[MAXPOSN * 8];
int		m;

int		max_cord, min_cord;

void
add( int x1, int x2, int y, int v ) {

	scn[m].x1 = x1;
	scn[m].x2 = x2;
	scn[m].y  = y;
	scn[m].v  = (char)v;

	m++;
}

void
chg_mx( int x1, int x2 ) {

	if ( x1 < min_cord ) min_cord = x1;
	if ( x2 > max_cord ) max_cord = x2;
}

void
cal_len( int tree, int lft, int rht ) {

	if ( val[tree] ) len[tree] = rht - lft;
	else
		if ( 1 + lft == rht ) len[tree] = 0;
		else len[tree] = len[ LFT(tree) ] + len[ RHT(tree) ];
}

void
update( int tree, int lft, int rht, int al, int ar, int av ) {

	if ( lft == al && ar == rht ) {
		
		val[tree] += av;
		cal_len( tree, lft, rht );

		return ;
	}

	int		mid;

	mid = ( lft + rht ) >> 1;

	if ( ar <= mid ) update( LFT(tree), lft, mid, al, ar, av );
	else if ( al >= mid ) update( RHT(tree), mid, rht, al, ar, av );
	else {
	
		update( LFT(tree), lft, mid, al, mid, av );
		update( RHT(tree), mid, rht, mid, ar, av );
	}
	cal_len( tree, lft, rht );
}

int
main() {

	int		n;

	int		x1, y1;
	int		x2, y2;
	int		x3, y3;
	int		x4, y4;
	
	int		i;

	llg		ans;

	while ( scanf("%d", &n), n ) {
	
		m = 0;
		memset(val, 0, sizeof(val));
		memset(len, 0, sizeof(len));

		max_cord = -1;
		min_cord = MAXCORD + 1;

		while ( n-- ) {
		
			scanf("%d%d%d%d%d%d%d%d", &x1, &y1, &x2, &y2, &x3, &y3, &x4, &y4);

			if ( x1 != x3 ) {
			
				add( x1, x3, y1, BTOM );
				add( x1, x3, y2, TOP );
				chg_mx( x1, x3 );
			}
			if ( x4 != x2 ) {
			
				add( x4, x2, y1, BTOM );
				add( x4, x2, y2, TOP );
				chg_mx( x4, x2 );
			}
			if ( y1 != y3 ) {
			
				add( x3, x4, y1, BTOM );
				add( x3, x4, y3, TOP );
			}
			if ( y4 != y2 ) {
			
				add( x3, x4, y4, BTOM );
				add( x3, x4, y2, TOP );
			}
			chg_mx( x3, x4 );
		}
		sort(scn, scn + m);

		ans = 0;
		i   = 0;
		while ( i < m ) {
		
			do {

				update( ROOT, min_cord, max_cord, scn[i].x1, scn[i].x2, scn[i].v );
				i++;
			} while ( i < m && scn[i].y == scn[i - 1].y );

			if ( i < m )
				ans += (llg)len[ROOT] * (llg)( scn[i].y - scn[i - 1].y );
		}

		printf("%I64d\n", ans);
	}

	return 0;
}

单词解释:


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值