算法原理:线段树
基础习题: http://poj.org/problem?id=1177
#include <iostream> #include <stdio.h> #include <algorithm> #define lson (x << 1) #define rson (x << 1 | 1) using namespace std; const int MAXN = 2e4; int n, X[MAXN << 1]; int x1, y1, x2, y2, pre = 0; /* 先初始化为 0 */ struct ScanLine { int l, r, h, mark; if(h == rhs.h) return mark > rhs.mark; return h < rhs.h; // 注意!这里是后来被 hack 掉以后加上去的 // 在此感谢 @leprechaun_kdl 指出问题 // 如果出现了两条高度相同的扫描线,也就是两矩形相邻 // 那么需要先扫底边再扫顶边,否则就会多算这条边 // 这个对面积并无影响但对周长并有影响 // hack 数据:2 0 0 4 4 0 4 4 8 输出应为:24 } line[MAXN]; struct SegTree { int l, r, sum, len, c; // c表示区间线段条数 bool lc, rc; // lc, rc分别表示左、右端点是否被覆盖 // 统计线段条数(tree[x].c)会用到 } tree[MAXN << 2]; void build_tree(int x, int l, int r) { tree[x].l = l, tree[x].r = r; tree[x].lc = tree[x].rc = false; tree[x].sum = tree[x].len = 0; tree[x].c = 0; if(l == r) return; int mid = (l + r) >> 1; build_tree(lson, l, mid); build_tree(rson, mid + 1, r); } void pushup(int x) { int l = tree[x].l, r = tree[x].r; if(tree[x].sum) { tree[x].len = X[r + 1] - X[l]; tree[x].lc = tree[x].rc = true; tree[x].c = 1; // 做好相应的标记 } else { tree[x].len = tree[lson].len + tree[rson].len; tree[x].lc = tree[lson].lc, tree[x].rc = tree[rson].rc; tree[x].c = tree[lson].c + tree[rson].c; // 如果左儿子左端点被覆盖,那么自己的左端点也肯定被覆盖;右儿子同理 if(tree[lson].rc && tree[rson].lc) tree[x].c -= 1; // 如果做儿子右端点和右儿子左端点都被覆盖, // 那么中间就是连续的一段,所以要 -= 1 } } void edit_tree(int x, int L, int R, int c) { int l = tree[x].l, r = tree[x].r; if(X[l] >= R || X[r + 1] <= L) return; if(L <= X[l] && X[r + 1] <= R) { tree[x].sum += c; pushup(x); return; } edit_tree(lson, L, R, c); edit_tree(rson, L, R, c); pushup(x); } ScanLine make_line(int l, int r, int h, int mark) { ScanLine res; res.l = l, res.r = r, res.h = h, res.mark = mark; return res; } // POJ 不这样做就会CE,很难受 int main() { scanf("%d", &n); for(int i = 1; i <= n; i++) { scanf("%d %d %d %d", &x1, &y1, &x2, &y2); line[i * 2 - 1] = make_line(x1, x2, y1, 1); line[i * 2] = make_line(x1, x2, y2, -1); X[i * 2 - 1] = x1, X[i * 2] = x2; } n <<= 1; sort(line + 1, line + n + 1); sort(X + 1, X + n + 1); int tot = unique(X + 1, X + n + 1) - X - 1; build_tree(1, 1, tot - 1); int res = 0; for(int i = 1; i < n; i++) { edit_tree(1, line[i].l, line[i].r, line[i].mark); res += abs(pre - tree[1].len); pre = tree[1].len; // 统计横边 res += 2 * tree[1].c * (line[i + 1].h - line[i].h); // 统计纵边 } res += line[n].r - line[n].l; // 特判一下枚举不到的最后一条扫描线 printf("%d", res); return 0; }