线段树求周长求交面积的做法

求周长的做法是对每个段,用线段树计算出不相交的区间个数,至于求区间个数,有点像扫描线的做法,也是先排序,排序之后就是求括号序列有多少个区间,很有技巧。然后就是段的长度*区间个数*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;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值