Advanced Data Structures :: Segment Tree
Description
把集合想象成区间来看这道题。
四种运算符,五种表达式,无尽的区间操作。
刚开始,你拥有的是一个空集。
输入并执行灰常多的区间操作。
输出最终你拥有的集合。
Type
Advanced Data Structures :: Segment Tree
Analysis
对于这道题目,如果想到线段树,
很容易的就可以把整个区间映射到线段树上(毕竟线段树也叫区间树)。
然而我们面对的第一个问题是,如果去操作我们的区间。
其实这个问题只是稍微费点脑力而已。
将每个叶子结点定为boolean,表示我们是否拥有这个点。
我们便有了一些基本的操作——将一段区间赋值为true,或者flase。
还有一种操作比较特别——将一段区间的值取反。
然后,对于题目给的五种命令,我们都可以找到对应的操作线段树的方法。
但是,这题还有第二个难点。
之所以一开头就把集合看成区间在于,这题所操作的元素,是线,而不是点。
这就导致像(1, 2)这种区间是有意义的,它表示了点1到点2不含顶点的线。
因此,线段树经常碰见的离散化问题又来了。
对于这种经典的,要把常规思维中的点变成线的问题,
我们的办法,就是将每个点倍增,这意味着,例如我们存储了{ 1, 2, 3, 4, 5}
代表了我们拥有的是(0,3),分开来讲,就是
(0, 1)、1、(1, 2)、2、(2, 3)。
如此一来,离散化工作便完成了。
还有一点需要注意,输入中的区间(a, b),由于a <= b,
可能出现类似(2, 2)这样的区间,应看成空。
Solution
// POJ 2528 // Mayor's posters // by A Code Rabbit #include <algorithm> #include <cstdio> #include <cstring> using namespace std; #define LSon(x) ((x) << 1) #define RSon(x) ((x) << 1 | 1) const int MAXN = 10002; const int ROOT = 1; struct Seg { int w; int flag; }; struct SegTree { Seg node[MAXN * 4 << 2]; void Build() { memset(node, 0, sizeof(node)); } void Push(int pos) { Seg& father = node[pos]; Seg& lson = node[LSon(pos)]; Seg& rson = node[RSon(pos)]; if (father.flag) { lson.flag = rson.flag = father.flag; lson.w = rson.w = father.flag; father.flag = 0; } } void Modify(int l, int r, int pos, int x, int y, int z) { if (x <= l && r <= y) { node[pos].w = z; node[pos].flag = z; return; } Push(pos); int m = l + r >> 1; if (x <= m) Modify(l, m, LSon(pos), x, y, z); if (y > m) Modify(m + 1, r, RSon(pos), x, y, z); } void Query(int l, int r, int pos, bool* bo) { if (l == r) { bo[node[pos].w] = true; return; } Push(pos); int m = l + r >> 1; Query(l, m, LSon(pos), bo); Query(m + 1, r, RSon(pos), bo); } }; struct Poster { int l, r; }; int n; Poster poster[MAXN]; int* pointer[MAXN << 2]; bool bo[MAXN]; SegTree tree; bool CMP(int* x, int* y) { return *x < *y; } int main() { int tot_case; scanf("%d", &tot_case); while (tot_case--) { // Input. scanf("%d", &n); for (int i = 0; i < n; i++) { scanf("%d%d", &poster[i].l, &poster[i].r); pointer[i * 2] = &poster[i].l; pointer[i * 2 + 1] = &poster[i].r; } // Discretization. sort(pointer, pointer + 2 * n, CMP); int num_bytes = 1; for (int i = 0; i < 2 * n - 1; i++) { int tmp = num_bytes; if (*pointer[i] != *pointer[i + 1]) { if (*pointer[i] + 1 == *pointer[i + 1]) num_bytes += 1; else num_bytes += 1; /* the correct answer should be: "num_bytes += 2;". */ } *pointer[i] = tmp; } *pointer[2 * n - 1] = num_bytes; // Stick posters by the segments tree. tree.Build(); int num_posters = 1; for (int i = 0; i < n; i++) tree.Modify(1, num_bytes, ROOT, poster[i].l, poster[i].r, num_posters++); // Compute and output. memset(bo, false, sizeof(bo)); tree.Query(1, num_bytes, ROOT, bo); int sum = 0; for (int i = 0; i < num_posters; i++) if (bo[i]) sum++; printf("%d\n", sum); } return 0; }