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 }

 

你可能感兴趣的:(poj)