poj2528 线段树覆盖
线段树覆盖+离散化
View Code
1 /* POJ2528 2 本题: 线段树+离散化 3 http://blog.csdn.net/tsaid/article/details/6665764 4 http://blog.csdn.net/lyy289065406/article/details/6799170 5 但是这题单纯用线段树去求解一样不会AC,原因是建立一棵[1,1QW]的线段树,其根系是非常庞大的,TLE和MLE是铁定的了。所以必须离散化。 6 通俗点说,离散化就是压缩区间,使原有的长区间映射到新的短区间,但是区间压缩前后的覆盖关系不变。举个例子: 7 有一条1到10的数轴(长度为9),给定4个区间[2,4] [3,6] [8,10] [6,9],覆盖关系就是后者覆盖前者,每个区间染色依次为 1 2 3 4。 8 现在我们抽取这4个区间的8个端点,2 4 3 6 8 10 6 9 9 然后删除相同的端点,这里相同的端点为6,则剩下2 4 3 6 8 10 9 10 对其升序排序,得2 3 4 6 8 9 10 11 然后建立映射 12 2 3 4 6 8 9 10 13 ↓ ↓ ↓ ↓ ↓ ↓ ↓ 14 1 2 3 4 5 6 7 15 那么新的4个区间为 [1,3] [2,4] [5,7] [4,6],覆盖关系没有被改变。新数轴为1到7,即原数轴的长度从9压缩到6,显然构造[1,7]的线段树比构造[1,10]的线段树更省空间,搜索也更快,但是求解的结果却是一致的。 16 离散化时有一点必须要注意的,就是必须先剔除相同端点后再排序,这样可以减少参与排序元素的个数,节省时间。 17 */ 18 #include <iostream> 19 #include <algorithm> 20 using namespace std; 21 #define W 10000005 22 #define N 10005 23 #define L(u) (u<<1) 24 #define R(u) (u<<1 | 1) 25 struct LineTree{ 26 int l, r, c; //c 标志位, 0表示没有, -1表示有多种颜色。 27 }tree[W]; 28 int l[N], r[N], arr[2*N]; 29 bool mark[W]; 30 int ans; 31 void build(int u, int l, int r){ //建树(u下标从1 开始) 32 tree[u].l = l; 33 tree[u].r = r; 34 tree[u].c = 0; 35 if (l == r) return ; 36 int mid = (l + r)>>1; 37 build ( L(u), l, mid); 38 build (R(u), mid+1, r); 39 } 40 void update(int u, int l, int r, int c){ // 41 if (tree[u].l > r || tree[u].r < l) //区间[l, r] 和区间[tree[u].l, tree[u].r] 无交集 42 return; //说明[l, r] 不被[tree[u].l, tree[u].r] 所在子树包含, 无需在向下搜索. 43 if (l <= tree[u].l && tree[u].r <= r){ //[l, r] 完全覆盖tree[u]的区间 44 tree[u].c = c; 45 return ; //由于tree[u] 根都覆盖了那么子树必然覆盖,所以就不需要继续往下搜索返回即可. 46 } 47 if (tree[u].c >= 0) { 48 tree[L(u)].c = tree[R(u)].c = tree[u].c; //由于不知道[l,r]覆盖了[tree[u].l, tree[u].r]多少, 49 tree[u].c = -1; //因此先由[tree[u].l, tree[u].r]的孩子继承[tree[u].l, tree[u].r]的单色 50 //c == -1 在查询的时候可以判定若等于-1 则该区间必然有多个颜色则往下搜索. 51 } //c == 0 表示该区间没有颜色. 52 update (L(u), l, r, c); //左孩子 53 update (R(u), l, r, c); //右孩子 54 } 55 56 void query(int u) 57 { 58 if (tree[u].c == 0) return; //无颜色返回 59 if (tree[u].c > 0){ 60 if (!mark[tree[u].c]){ 61 mark[tree[u].c] = true; 62 ans ++; 63 } 64 } 65 if (tree[u].c == -1){ //-1 表示该区间有多个颜色 66 query (L(u)); 67 query (R(u)); 68 } 69 } 70 //离散化 71 int fid(int l, int r, int num) { //二分查找 72 while (l <= r){ 73 int mid = (l + r)>>1; 74 if (arr[mid] == num) return mid; 75 if (arr[mid] < num) 76 l = mid +1; 77 else r = mid -1; 78 } 79 return -1; 80 } 81 int main(){ 82 int c, n; 83 int i, j; 84 cin>>c; 85 while (c--){ 86 cin>>n; 87 memset(mark, false, sizeof(mark)); 88 ans = 0; 89 for (i = 1; i <= n; i++){ 90 cin>>l[i]>>r[i]; 91 arr[i] = l[i]; 92 arr[i+n] = r[i]; 93 } 94 sort(arr+1, arr+1+2*n); 95 j = 1; 96 for (i=2; i<=2*n; i++) //剔除相同的元素 97 { 98 if (arr[i] != arr[i-1]) 99 arr[++j] = arr[i]; 100 } 101 build(1, 1, j); //建树 102 for (i=1; i<=n; i++){ 103 int a = fid(1, j, l[i]); 104 int b = fid(1, j, r[i]); 105 update(1, a, b, i); //更新 106 } 107 query(1); //从第一层开始查找 108 cout<<ans<<endl; 109 } 110 return 0; 111 }