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

 

你可能感兴趣的:(post)