1. 程式人生 > >【線段樹 + 離散化 + 詳細註釋】北大 poj 2528 Mayor's posters

【線段樹 + 離散化 + 詳細註釋】北大 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  : 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 ;
}