【線段樹 + 離散化 + 詳細註釋】北大 poj 2528 Mayor's posters
阿新 • • 發佈:2019-01-23
/* 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 ; }