求周长的做法是对每个段,用线段树计算出不相交的区间个数,至于求区间个数,有点像扫描线的做法,也是先排序,排序之后就是求括号序列有多少个区间,很有技巧。然后就是段的长度*区间个数*2,然后对矩形坐标进行关于y=x对称,再做一次就是完整的周长了。 受求周长做法的启发,可以得到求矩形交面积的做法,需要一个一维线段树,记录x方向线段的信息,还要一个二维线段树记录各部分是否被覆盖,一维线段树在某线段处add=1且扫描的线段为+时记录下y的位置,到add=2且扫描线段为-时记录下y的位置,这两个y以及x的线段就是交的部分,放入二维线段树中。这样循环完了之后再计算出二维线段树包含的面积。 周长的程序我写了(POJ1177),感觉学了不少,这里的线段树比一般的线段树更精妙些,不预先建立,需要时才建立,可以没有左孩子或右孩子,这并非是为了效率,而是为了便于左右孩子继承节点的add值。 矩形交面积的做法只是推想而已。 #include <cstdio> #include <algorithm> using namespace std; long n; struct tagRect { short left,top,right,bottom; }rects[5000]; short xMin,xMax,yMin,yMax; short ins_left, ins_right, ins_add; struct tagEdge { short y; short left,right; short add; inline void Set(short _y, short _left, short _right, short _add) { y = _y; left = _left; right = _right; add = _add; } inline bool operator < (const tagEdge& edge) const { //注意!直接return y < edge.y可以ac,但是对于一些特殊情况,可能计算错误 if (y < edge.y) { return true; } else if (y > edge.y) { return false; } else { if (add > 0 && edge.add < 0) { return true; } else { return false; } } return y < edge.y; } }edges[10000]; struct tagSegTreeNode { short left,right,add; bool blc;//是否有左孩子 bool brc;//是否有右孩子 inline void Set(short _left, short _right, short _add) { left = _left; right = _right; add = _add; blc = brc = false; } }segtree[1 << 16]; void input(void) { scanf("%d", &n); xMin = yMin = (short)0x7fff; xMax = yMax = (short)0x8000; for (long i = 0; i < n; ++i) { scanf("%d%d%d%d", &rects[i].left, &rects[i].top, &rects[i].right, &rects[i].bottom); if (rects[i].left < xMin) xMin = rects[i].left; if (rects[i].right > xMax) xMax = rects[i].right; if (rects[i].top < yMin) yMin = rects[i].top; if (rects[i].bottom > yMax) yMax = rects[i].bottom; } } long insert(long pos) { tagSegTreeNode& node = segtree[pos]; long retVal = 0; if ((ins_left <= node.left && node.right <= ins_right) && (!node.blc && !node.brc)) { node.add += ins_add; retVal = (!node.add) ? (node.right - node.left) : 0; return retVal; } short mid = ((node.left + node.right) >> 1); if (ins_right <= mid) { pos <<= 1; if (!node.blc) { node.blc = true; segtree[pos].Set(node.left, mid, node.add); } return insert(pos); } else if (ins_left >= mid) { pos <<= 1; ++pos; if (!node.brc) { node.brc = true; segtree[pos].Set(mid, node.right, node.add); } return insert(pos); } pos <<= 1; if (!node.blc) { node.blc = true; segtree[pos].Set(node.left, mid, node.add); } retVal = insert(pos); ++pos; if (!node.brc) { node.brc = true; segtree[pos].Set(mid, node.right, node.add); } retVal += insert(pos); return retVal; } inline long insert(long pos, short left, short right, short add) { ins_left = left; ins_right = right; ins_add = add; return insert(pos); } long CalcHorzLen(void) { long i; for (i = 0; i < n; ++i) { edges[i].Set(rects[i].top, rects[i].left, rects[i].right, 1); edges[i + n].Set(rects[i].bottom, rects[i].left, rects[i].right, -1); } sort(edges, edges + (n << 1)); long retVal = 0; segtree[1].Set(xMin, xMax, 0); for (i = 0; i < (n << 1); ++i) { retVal += insert(1, edges[i].left, edges[i].right, edges[i].add); } return retVal; } int main(void) { input(); long l = CalcHorzLen(); for (long i = 0; i < n; ++i) { swap(rects[i].left, rects[i].top); swap(rects[i].right, rects[i].bottom); } swap(xMin, yMin); swap(xMax, yMax); l += CalcHorzLen(); l <<= 1; printf("%d/n", l); return 0; }