`

【线段树 + 离散化 + 详细注释】北大 poj 2528 Mayor's posters

阅读更多
第二次
/* THE PROGRAM IS MADE BY PYY */
/*----------------------------------------------------------------------------//
    Copyright (c) 2011 panyanyany All rights reserved.

    URL   : http://poj.org/problem?id=2528
    Name  : 2528 Mayor's posters

    Date  : Saturday, October 1, 2011
    Time Stage : one and half an hours

    Result: 
9378439	panyanyany
2528
Accepted	1248K	79MS	C++
3804B	2011-10-01 00:36:01
9378423	panyanyany
2528
Wrong Answer			C++
3707B	2011-10-01 00:30:47
								

Test Data :

Review :
第二次做,还是有很多地方没有注意到的。比如 2 * n,以及递归到达叶子时没有判断 和 退出
//----------------------------------------------------------------------------*/

#include <stdio.h>
#include <string.h>
#include <algorithm>

using namespace std ;

typedef __int64 LL ;

int max (int lhs, int rhs)
{
	return (lhs > rhs ? lhs : rhs) ;
}

int min (int lhs, int rhs)
{
	return lhs < rhs ? lhs : rhs ;
}

#define MAXSIZE 10010

#define L(n) 		((n) << 1)
#define R(n) 		(((n) << 1) + 1)
#define MID(a, b) 	(((a) + (b)) >> 1)

// 此结构体用来映射新的端点值
typedef struct {
	bool	isLeft ;	// 判断此端点是否左端点
	int 	id ;		// 标记此站点是第几个端点
	int		val ;		// 此端点的原来的值
} MAP ;

// 此结构体用来做线段树的节点
typedef struct {
	int		id ;			// 海报序号,若为0则表示在[left,right]内海报的张数不确定
	int		left, right ;	// left , right 分别代表此区间的 左,右 端点
} NODE ;

int		tcase, n, cnt ;
bool	flag[MAXSIZE] ;
int		left[MAXSIZE], right[MAXSIZE] ;
MAP		map[MAXSIZE * 2] ;
NODE	tree[MAXSIZE * 14] ;

int cmp(MAP lhs, MAP rhs)
{
	return lhs.val < rhs.val ;
}

void build (int node, int left, int right)
{
	tree[node].id 		= 0 ;
	tree[node].left		= left ;
	tree[node].right	= right ;
	
	if (left == right)
		return ;
	int mid = MID (left, right) ;
	build (L(node), left, mid) ;
	build (R(node), mid + 1, right) ;
}

void update (int node, int left, int right, int NewId)
{
	// 若张贴的海报刚好覆盖当前区间,则当前区间的 id 为该海报的 id
	if (tree[node].left == left && tree[node].right == right ||
		tree[node].left == tree[node].right)
	{
		tree[node].id = NewId ;
		return ;
	}
	
	// 若海报不足以完全覆盖当前区间
	
	// 若此区间之前已经被某海报完全覆盖
	if (tree[node].id != 0)
	{
		tree[L(node)].id = tree[node].id ;		// 假设左子树不被海报覆盖,则其 id 为根区间的 id
		tree[R(node)].id = tree[node].id ;		// 假设右子树不被海报覆盖,则其 id 为根区间的 id
		tree[node].id	 = 0 ;					// 因为在此区间内有两张海报,则此区间的 id 为不确定
	}
	
	int mid = MID (tree[node].left, tree[node].right) ;
	
	// 若海报仅能覆盖左半区间
	if (right <= mid)
		update (L(node), left, right, NewId) ;
	// 若海报仅能覆盖右半区间
	else if (mid < left)
		update (R(node), left, right, NewId) ;
	// 若海报横跨本区间中部
	else
	{
		update (L(node), left, mid, NewId) ;
		update (R(node), mid + 1, right, NewId) ;
	}
}

void count (int node)
{
	// 若此区间恰有一张海报
	if (tree[node].id)
	{
		// 若此海报未被记录
		if (flag[tree[node].id] == false)
		{
			flag[tree[node].id] = true ;
			++cnt ;
		}
		return ;
	}
	
	// 已经到达叶子,此节点仍为不确定状态,则退出
	if (tree[node].left == tree[node].right)
		return ;
	
	// 若此区间的海报数量不确定
	// 则递归查找子区间
	count (L(node)) ;
	count (R(node)) ;
}

int main ()
{
	int	i, j ;
	int t, tmp ;
	while (scanf ("%d", &tcase) != EOF)
	{
		while (tcase--)
		{
			scanf ("%d", &n) ;
			for (i = 1 ; i <= n ; ++i)
			{
				scanf ("%d%d", &left[i], &right[i]) ;
				map[i * 2 - 1].id 		= i ;
				map[i * 2 - 1].isLeft 	= true ;
				map[i * 2 - 1].val		= left[i] ;
				
				map[i * 2].id			= i ;
				map[i * 2].isLeft		= false ;
				map[i * 2].val			= right[i] ;
			}
			// 把端点值按从小到大排序, 注意 2 * n
			sort (map + 1, map + 1 + 2 * n, cmp) ;
			
			// 离散化
			tmp = map[1].val ;	// tmp 用来判断是否与前一个原始端点值相同
			t = 1 ;				// t 作为新的端点值(离散化)
			
			// 注意 2 * n
			for (i = 1 ; i <= 2 * n ; ++i)
			{
				if (tmp != map[i].val)
				{
					tmp = map[i].val ;
					++t ;
				}
				if (map[i].isLeft == true)
				{
					left[map[i].id] = t ;
				}
				else
				{
					right[map[i].id] = t ;
				}
			}
			// 注意 2 * t
			build (1, 1, 2 * t) ;	// 构造线段树
			// 更新线段树
			for (i = 1 ; i <= n ; ++i)
				update (1, left[i], right[i], i) ;
				
			memset (flag, false, sizeof (flag)) ;
			cnt = 0 ;
			count (1) ;
			printf ("%d\n", cnt) ;
		}
	}
	return 0 ;
}



第一次
/* THE PROGRAM IS MADE BY PYY */
/*----------------------------------------------------------------------------//
    Copyright (c) 2011 panyanyany All rights reserved.

    URL   : http://poj.org/problem?id=2528
    Name  : 2528 Mayor's posters

    Date  : Monday, September 26, 2011
    Time Stage : Three hours

    Result: 
9363927	panyanyany
2528
Accepted	1252K	94MS	C++
3304B	2011-09-26 21:12:59
9363885	panyanyany
2528
Accepted	1272K	94MS	C++
3738B	2011-09-26 21:05:46
9363864	panyanyany
2528
Wrong Answer			C++
3616B	2011-09-26 21:01:08
9363854	panyanyany
2528
Wrong Answer			C++
3612B	2011-09-26 20:59:25
9363846	panyanyany
2528
Compile Error
		C++
3544B	2011-09-26 20:58:31
9363671	panyanyany
2528
Time Limit Exceeded			C++
3370B	2011-09-26 20:16:14
9363662	panyanyany
2528
Runtime Error			C++
3369B	2011-09-26 20:15:02
9363631	panyanyany
2528
Runtime Error			C++
3337B	2011-09-26 20:09:37
9363613	panyanyany
2528
Runtime Error			C++
3341B	2011-09-26 20:06:49
9363610	panyanyany
2528
Runtime Error			C++
3332B	2011-09-26 20:06:02
9363604	panyanyany
2528
Runtime Error			C++
3332B	2011-09-26 20:05:09

Test Data :

Review : 
首先感谢几位大牛的解题报告:
AC_Von,http://www.cnblogs.com/vongang/archive/2011/08/10/2133869.html
kuangbin,http://www.cnblogs.com/kuangbin/archive/2011/08/15/2139931.html

思路:

这题的思想,主要是离散化,当然还有一些小技巧。
离散化,大概就是:
	由于不可能开1000,0000的数组,又注意到其实际有效数据才
10000 对,在最坏的情况下,将有 2000 个不同的数据。
比如:
n = 5 ;
1,200,3,5000,8999,100000, 999,9999999999
若直接以上面的数字作下标,则起码要开 9999999999 大小的数组,
显然是不可能的。
若将上面的数列排序,则:
1,3,200,999,5000,8999,100000,9999999999
根据排序的顺序,映射新的下标:
1,3,200,999,5000,8999,100000,9999999999
1,2,3,  4,  5,   6,   7,     8
则我们其实只开 8 大小的数组就可以了!

血泪史:

一开始数组开小了,无限RE,
后来用一个循环去遍历整棵树,TLE,
于是将 count() 函数的查找改为递归模式,同时修改 update() 函数,正式确定
Tree[node].sum 的作用,当为 0 时,表示当前区间的海报数量不确定,
当为正整数值时,表示当前区间只有某海报
后来VC6或者VA的复制发了神经,后面截断一大片,又CE,
重新复制后,WA,
最后一次改错,是标记重复

//----------------------------------------------------------------------------*/

#include <stdio.h>
#include <string.h>
#include <algorithm>

using namespace std ;

typedef __int64 LL ;

int max (int lhs, int rhs)
{
	return (lhs > rhs ? lhs : rhs) ;
}

int min (int lhs, int rhs)
{
	return lhs < rhs ? lhs : rhs ;
}

#define MAXSIZE 10010

#define L(n)	((n) << 1)
#define R(n)	(((n) << 1) + 1)

typedef struct {
	int		id ;
	int		endVal ;
	bool	isLeft ;
} TAG ;

typedef struct {
	int id ;
	int left, right ;
} NODE ;

bool	flag[MAXSIZE * 2] ;
int		tcase, n, cnt ;
int		num[MAXSIZE][2] ;
TAG		tag[MAXSIZE * 2] ;
NODE	tree[10 * (MAXSIZE)] ;

int cmp (TAG ta, TAG tb)
{
	return ta.endVal < tb.endVal ;
}

void build (int node, int left, int right)
{
	tree[node].left	 = left ;
	tree[node].right = right ;
	// 0 表示当前区间的海报数量不确定,正整数则表示当前区间完全被某海报覆盖
	tree[node].id	 = 0 ;

	if (tree[node].left == tree[node].right)
		return ;

	int mid = (left + right) / 2 ;
	build (L(node), left, mid) ;
	build (R(node), mid + 1, right) ;
}

void update (int node, int left, int right, int id)
{
	// 若新的海报可以覆盖当前区间
	if (left <= tree[node].left && tree[node].right <= right)
	{
		tree[node].id = id ;
		return ;
	}

/*
	// [left, right] 与 node 所在区间无交集
	if (tree[node].right < left || right < tree[node].left)
		return ;
*/
	// 若当前区间已经被海报覆盖过,且新的海报只能覆盖部分区间
	if (tree[node].id > 0)
	{
		tree[L(node)].id = tree[node].id ;
		tree[R(node)].id = tree[node].id ;
		// 重新将此区间的海报数量置于不确定状态
		tree[node].id = 0 ;
	}

	// [left, right] 与 node 所在区间有交集
	int mid = (tree[node].left + tree[node].right) / 2 ;

	// [left, right] 在 node 左半区间
	if (right <= mid)
		update (L(node), left, right, id) ;
	// [left, right] 在 node 右半区间
	else if (mid < left)
		update (R(node), left, right, id) ;
	// [left, right] 在 node 两半都有, 即在 node 中部
	else
	{
		update (L(node), left, mid, id) ;
		update (R(node), mid + 1, right, id) ;
	}

	return ;
}


void count (int node)
{
	// 表示此区间只有一张编号为 id 的海报
	if (tree[node].id > 0)
	{
		// 此海报是否已经计算过
		if (flag[tree[node].id] == false)
		{
			++cnt ;
			flag[tree[node].id] = true ;
		}
		return ;
	}
	// 已经到达叶子,此节点仍为不确定状态,则退出
	if (tree[node].left == tree[node].right)
		return ;

	// 对于不确定的树,向左右两边搜索
	count (L(node)) ;
	count (R(node)) ;
}

int main ()
{
	int i, j ;
	while (scanf ("%d", &tcase) != EOF)
	{
		while (tcase--)
		{
			memset (flag, 0, sizeof (flag)) ;
			scanf ("%d", &n) ;
			for (i = 1 ; i <= n ; ++i)
			{
				scanf ("%d%d", &num[i][0], &num[i][1]) ;
				tag[2 * i - 1].id		= i ;
				tag[2 * i - 1].isLeft	= true ;
				tag[2 * i - 1].endVal	= num[i][0] ;

				tag[2 * i].id		= i ;
				tag[2 * i].isLeft	= false ;
				tag[2 * i].endVal	= num[i][1] ;
			}
			sort (tag + 1, tag + 2 * n + 1, cmp) ;

			int t = 1, tmp = tag[1].endVal ;
			for (i = 1 ; i <= 2 * n ; ++i)
			{
				// 本来以为这一句不加也没关系,结果WA了,跳过重复的值,
				if (tmp != tag[i].endVal)
				{
					tmp = tag[i].endVal ;
					++t ;
				}
				if (tag[i].isLeft == true)
					num[tag[i].id][0] = t ;
				else
					num[tag[i].id][1] = t ;
			}

			build (1, 1, 2 * n) ;

			for (i = 1 ; i <= n ; ++i)
			{
				update (1, num[i][0], num[i][1], i) ;
			}
			cnt = 0 ;
			count (1) ;
			printf ("%d\n", cnt) ;
		}
	}
	return 0 ;
}

0
8
分享到:
评论

相关推荐

    POJ2528-Mayor's posters【区间映射压缩(离散化)+线段树】

    POJ2528-Mayor's posters 【区间映射压缩(离散化)+线段树】 解题报告+AC代码 http://hi.csdn.net/!s/83XZJU ========&gt; 我的所有POJ解题报告链接: http://blog.csdn.net/lyy289065406/article/details/6642573

    poj-2528 Mayor's posters 测试数据及答案

    【标题】"poj-2528 Mayor's posters 测试数据及答案"涉及的是一个编程竞赛中的问题,这个问题来源于POJ(Programming Online Judge)平台,编号为2528。POJ是一个在线的编程练习系统,它提供了一系列的问题供程序员...

    POJ2528-Mayor's posters 测试数据

    【标题】"POJ2528-Mayor's posters"是一个经典的编程竞赛题目,源自2003年Alberta Collegiate Programming Contest。该比赛每年都会吸引众多编程爱好者参加,旨在锻炼参赛者的算法设计和实现能力。题目“市长的海报...

    线段树练习POJ 3264

    线段树是一种数据结构,常用于处理区间查询与更新的问题,它能高效地支持动态维护区间最值、求和等问题。在本题“POJ 3264”中,我们可能面临的是一个区间最值或者区间求和的计算任务。线段树的基本思想是将数组或...

    线段树入门学习(二)(兼解POJ3468) JAVA

    在本教程中,我们将深入探讨线段树的基本概念,以及如何使用Java实现线段树来解决实际问题,如POJ3468题目。 首先,理解线段树的核心在于它的分治思想。线段树将一个大区间(通常是一维数组)分成多个小区间,每个...

    POJ3277.rar_3277 poj_poj3277_多个面积_线段树

    标题中的“POJ3277.rar_3277 poj_poj3277_多个面积_线段树”是指一个与编程竞赛相关的压缩文件,特别提到了POJ(Problemset Online Judge)的第3277号问题。POJ是一个著名的在线编程竞赛平台,参与者需要解决各种...

    线段树入门学习(三)懒操作(兼解POJ1823) JAVA

    在本篇博文中,我们将深入探讨线段树的概念,以及如何实现懒惰更新(Lazy Propagation)策略,同时结合POJ1823问题进行实战应用。懒惰更新是优化线段树性能的关键技巧,它避免了频繁地对每个节点进行更新,从而减少...

    poj 2352 stars(树状数组,线段树)

    ### POJ 2352 Stars - 树状数组与线段树实现 #### 题目背景 POJ 2352 Stars 是一道经典的算法题目,它提供了使用树状数组或线段树来解决的可能性。这两种数据结构都是为了高效处理区间查询问题而设计的。在这篇...

    POJ1159-Palindrome

    【标题】"POJ1159-Palindrome" 是北京大学在线编程平台POJ上的一道编程题目。这道题目主要考察的是字符串处理和回文判断的知识点。 【描述】"北大POJ1159-Palindrome 解题报告+AC代码" 暗示了解决这道问题的方法和...

    poj2823.cpp.tar.gz_线段树

    在`poj2823.cpp`源代码中,我们可以看到实现线段树的具体细节,包括如何初始化、更新以及查询线段树。此外,代码可能还包括了问题的输入输出处理,错误检查,以及可能的优化策略,比如lazy propagation(惰性传播)...

    北大POJ部分题目答案(一些基础题目)

    很多的POJ题目答案!1000~1008,1011~1014,1016,1017,1019,1028,1032,1045,1046,1047,1050,1061,1067,1068,1088,1102,1159,1163,1183,1207,1218,1226,1247,1256,1258,1298,1316,1323,...

    北大acm题解(poj题目分析)

    《北大ACM题解》是一本专为解决POJ(Programming Online Judge)平台上的编程竞赛题目而编写的指导书籍。ACM(国际大学生程序设计竞赛,International Collegiate Programming Contest)是一项全球性的比赛,旨在...

    北大POJ经典结题报告

    《北大POJ经典结题报告》是一份针对北京大学在线编程平台POJ的解题报告,旨在帮助学习者掌握算法和编程技巧,提升解决问题的能力。POJ(Problem Online Judge)是北大的一个在线编程竞赛系统,提供了丰富的算法题目...

    线段树题目

    - **POJ 2528**:这是一道稍微复杂一点的线段覆盖问题,需要使用离散化技术。题目中的输入数据范围可能很大,直接存储会消耗大量的内存空间。因此,首先需要对所有涉及的点进行离散化处理,压缩点的范围,减少所需的...

    北大POJ 大量解题代码

    【标题】"北大POJ大量解题代码"指的是北京大学在线编程平台POJ上的一系列算法题目解决方案的集合。这些代码通常由ACM(国际大学生程序设计竞赛)参赛者或爱好者编写,旨在帮助学习者理解并解决各类算法问题,提高...

    北大poj解题报告

    北京大学在线编程平台POJ(PKU Online Judge)是许多学习算法和进行编程训练的学生们的重要实践场所。这个“北大poj解题报告”包含了作者在使用POJ平台解题过程中的学习总结和经验分享,旨在帮助软件工程专业的学生...

    北京大学poj题目类型分类

    北京大学POJ题目类型分类 北京大学POJ题目类型分类是根据题目的难易程度、知识领域和解决方法对POJ题目进行分类的。通过对POJ题目的分类,可以帮助学习者更好地理解和掌握算法和数据结构的知识。 简单题 简单题是...

    ACM常用算法及其相应的练习题.docx

    + poj2528, poj2828, poj2777, poj2886, poj2750 * 静态二叉检索树 + poj2482, poj2352 * 树状树组 + poj1195, poj3321 * RMQ + poj3264, poj3368 * 并查集的高级应用 + poj1703, poj2492 * KMP算法 + poj1961...

    POJ 部分题解 解题报告

    10. **POJ 2528 线段树.txt**:这是第三个线段树相关的题目,解题报告可能会深入讲解线段树在不同场景下的应用。 通过阅读这些解题报告,你可以学习到各种高级算法的应用,如动态规划、搜索算法、数据结构(如线段...

Global site tag (gtag.js) - Google Analytics