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 }

 

posted @ 2013-04-13 14:51  旅行的蜗牛  阅读(197)  评论(0编辑  收藏  举报