题目: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 ", ans); 144 } 145 return 0; 146 }