POJ 2528 Mayor's posters 线段树+离散化

题目:http://poj.org/problem?id=2528

很经典的题目,线段树入门题,写了好几天终于懂离散化了。

题意是在一个被分成 10000000 段的墙上贴海报,从L贴到R,问最后能看见几张海报。

因为海报只能是后来的覆盖先来的,所以逆序贴,每贴一张就标记它表示的线段,这样当贴到第i张时,如果表示的线段都被标记过了,说明这张不会被看到。注意离散化时如果相邻两点相差 > 1,要插入额外点,表示之间有间隔,更多细节在注释中。

  1 #include <stdio.h>
  2 #include <string.h>
  3 #include <algorithm>
  4 
  5 const int MAXN = 20010;
  6 
  7 int dic[MAXN<<1];
  8 bool isview;
  9 
 10 //每张海报表示的线段
 11 struct Segment
 12 {
 13     int left;
 14     int right;
 15     int num;
 16 } seg[MAXN];
 17 
 18 struct Tree_Node
 19 {
 20     int left;
 21     int right;
 22     bool flag; //有没有被覆盖
 23 } tree[MAXN<<3];
 24 
 25 void build(int left, int right, int step)
 26 {
 27     tree[step].left = left;
 28     tree[step].right = right;
 29     tree[step].flag = 0;
 30     if(left == right)
 31         return;
 32     int mid = (left + right) >> 1;
 33     build(left, mid, step<<1);
 34     build(mid+1, right, step<<1|1);
 35 }
 36 
 37 void insert(int left, int right, int step)
 38 {
 39     int mid = (tree[step].left + tree[step].right) >> 1;
 40     if(tree[step].left == left && tree[step].right == right)
 41     {
 42         if(!tree[step].flag)
 43         {
 44             //如果没有被覆盖,标记isview为真
 45             isview = 1;
 46             
 47             //同时标记子节点
 48             if(tree[step].left != tree[step].right)
 49             {
 50                 insert(left, mid, step<<1);
 51                 insert(mid+1, right, step<<1|1);
 52             }
 53         }
 54         tree[step].flag = 1;
 55         return;
 56     }
 57     if(tree[step].left == tree[step].right)
 58         return;
 59     if(mid >= right)
 60     {
 61         insert(left, right, step<<1);
 62     }
 63     else if(mid < left)
 64     {
 65         insert(left, right, step<<1|1);
 66     }
 67     else
 68     {
 69         insert(left, mid, step<<1);
 70         insert(mid+1, right, step<<1|1);
 71     }
 72     
 73     //这里是重点,如果两个子结点都被标记了,则标记当前父节点
 74     if(tree[step<<1].flag && tree[step<<1|1].flag)
 75     {
 76         tree[step].flag = 1;
 77     }
 78 }
 79 
 80 int search(int low, int high, int key)
 81 {
 82     while(low <= high)
 83     {
 84         int mid = (low + high) >> 1;
 85         if(dic[mid] < key)
 86             low = mid + 1;
 87         else if(dic[mid] > key)
 88             high = mid - 1;
 89         else
 90             return mid;
 91     }
 92 }
 93 
 94 int cmp(const struct Segment &x, const struct Segment &y)
 95 {
 96     return x.num > y.num;
 97 }
 98 
 99 int main()
100 {
101     int t, n;
102     int tmp[MAXN];
103     scanf("%d", &t);
104     while(t--)
105     {
106         scanf("%d", &n);
107         for(int i = 0; i < n; i++)
108         {
109             scanf("%d %d", &seg[i].left, &seg[i].right);
110             seg[i].num = i;
111             tmp[i<<1] = seg[i].left;
112             tmp[i<<1|1] = seg[i].right;
113         }
114         
115         //离散化
116         std::sort(tmp, tmp + (n<<1));
117         int sum = 0;
118         dic[0] = tmp[0];
119         for(int i = 1; i < (n<<1); i++)
120         {
121             if(tmp[i] != dic[sum])
122             {
123                 //加额外线段
124                 if(tmp[i] - dic[sum] > 1)
125                     dic[++sum] = tmp[i] - 1;
126                 dic[++sum] = tmp[i];
127             }
128         }
129 
130         int ans = 0;
131         build(0, sum, 1);
132         //按张贴前后逆序排序
133         std::sort(seg, seg+n, cmp);
134         for(int i = 0; i < n; i++)
135         {
136             int left = search(0, sum, seg[i].left);
137             int right = search(0, sum, seg[i].right);
138             //假设当前线段不能看到(即被完全覆盖)
139             isview = 0;
140             insert(left, right, 1);
141             if(isview)ans++;
142         }
143         printf("%d\n", ans);
144     }
145     return 0;
146 }
View Code

 

posted @ 2013-11-03 21:48  Anti-Magic  阅读(199)  评论(0编辑  收藏  举报