POJ:http://poj.org/problem?id=2528
這兩天在學習線段樹演算法,參考了網上許多線段樹的資料,用Google搜一下,就能搜到很多,這裡就不詳細解釋什麼事線段樹了。
線段樹在解決區間重合問題,比如求一段區間,最大值,最小值,求和等問題非常有效,一次查詢的時間複雜度是log(n),時間複雜度非常理想。
題目大意:在牆壁上貼廣告,廣告的版面有大有小,並且貼廣告有先後之分,後面貼的廣告會覆蓋前面的廣告,求解最後能看到的廣告面,如所示:
兩種視圖,最後從From View能看見的廣告數目是4。
題目分析:這裡面不僅用到了線段樹,還用到了離散化方法,意思是將區間範圍很大的資料集映射到較小的資料集,這樣構造線段樹更加高效,具體方法就是把所有線段的端點排序,從左至右,一次編號(兩條線段相同的端點,用同一個編號),並且將新的編號替代原來的端點值,這樣既不改變各個線段的性質,同時能夠縮小資料範圍。
具體代碼如下:
/* * my_poj2528.cpp * * Created on: 2012-6-15 * Author: ict */#include <cstring>#include <cstdlib>#include <cstdio>#include <algorithm>using namespace std;#define MAX 20010#define LC(x) ((x) << 1)//計算左孩子位置#define RC(x) (((x) << 1) + 1)//計算右孩子位置struct Node{int l;//線段左端點值int r;//線段右端點int c;//線段的顏色}nodes[MAX * 5];int map[MAX][2];//記錄輸入線段的左右兩個端點int record[MAX];//記錄顏色是否已經出現int total;//用於離散化struct Line{int point;//記錄端點的座標int num;//記錄原來的編號}line[MAX * 2];int mycmp(const Line &a, const Line &b){return a.point < b.point;}void buildTree(int l, int r, int position){nodes[position].l = l;nodes[position].r = r;nodes[position].c = 0;//初始化顏色都為0if(l == r)return ;int mid = (l + r) >> 1;buildTree(l, mid, LC(position));//構造左子樹buildTree(mid + 1, r, RC(position));//構造右子樹return ;}/* * @para l, r: 線段的左、右端點 * @para position: 插入樹的位置 * @para color: 線段的顏色 */void insert(int l, int r, int position, int color){if(nodes[position].l == l && nodes[position].r == r)//剛好覆蓋,修改顏色,直接返回{nodes[position].c = color;return ;}if(nodes[position].c > 0)//如果當前線段已經有顏色,先將顏色複製給左右兩個子樹,非常重要{nodes[LC(position)].c = nodes[position].c;nodes[RC(position)].c = nodes[position].c;nodes[position].c = 0;//標記線段沒有顏色}if(l >= nodes[RC(position)].l)//完全在右子樹insert(l, r, RC(position), color);elseif(r <= nodes[LC(position)].r)//完全在左子樹insert(l, r, LC(position), color);else//兩個子樹都有{insert(l, nodes[LC(position)].r, LC(position), color);insert(nodes[RC(position)].l, r, RC(position), color);}}/* * @para position: 查詢指定線段的顏色 */void update(int position){if(nodes[position].c != 0)//如果當前線段有顏色,記錄,並且直接返回{if(!record[nodes[position].c]){total++;record[nodes[position].c] = 1;}return ;}//如果當前線段沒有顏色,遞迴調用左右子樹,查詢顏色update(LC(position));update(RC(position));return ;}int main(){int number;int n;int i;scanf("%d", &number);while(number--){scanf("%d", &n);for(i = 0; i < n; i++){scanf("%d%d", &map[i][0], &map[i][1]);line[2*i].point = map[i][0];//記錄資料,用於離散化line[2*i + 1].point = map[i][1];line[2*i].num = -(i + 1);//線段第一個端點用負數記錄line[2*i + 1].num = i + 1;//線段第二個端點用正數記錄}//首先將所有端點排序sort(line, line + 2*n, mycmp);int temp = line[0].point;int count = 1;//重新開始編號,從1開始for(i = 0; i < 2*n; i++){if(temp != line[i].point)//如果當前端點和前面的端點不一樣,編號值+1{count++;temp = line[i].point;}if(line[i].num < 0)map[-line[i].num - 1][0] = count;elsemap[line[i].num - 1][1] = count;}buildTree(1, count, 1);for(i = 0; i < n; i++)insert(map[i][0], map[i][1], 1, i + 1);memset(record, 0, sizeof(record));total = 0;update(1);printf("%d\n", total);}return 0;}