现在的位置: 首页 > 综合 > 正文

poj 1177 Picture(线段树求矩形周长并)

2013年12月09日 ⁄ 综合 ⁄ 共 2614字 ⁄ 字号 评论关闭

折腾快一天了都,磕磕绊绊地看看人家代码看看解释,自己画图输出中间过程,差不多都理解了, = = T T。。。

应该算是有两种做法。

1、直接离散化后先算平行于 Y 轴的矩形并后的线段,然后算X轴的。这个就是插入一条线段,删除一条线段,代码比较长。

2、陈宏WC99论文《数据结构的选择与算法效率——从IOI98试题PICTURE谈起》,太长了,没怎么看懂。不过从网上搜了些这题的题解,大致看懂了。

我就说下我对这种做法的理解,比较水。。。

这个题的线段树需要更新多个域。就我的代码来讲,Tnode(也就是我的线段树节点)中的:

1、l,r 跟往常线段树一样,是离散化后的下标。

2、len,当前节点的子树中覆盖的线的长度。

3、num,子树中被分为几段,这个主要是为了计算平行于X轴的线段长度。

4、cover,标记当前节点是否被覆盖。

5、rb lb 是标记当前节点的右端点和左端点是否被覆盖。找num的数量需要用到。

Sline结构体是扫描线,其中的flag标记是入边还是出边。

具体过程:前期处理过程和矩形面积并没啥区别(不会的话先去学那个,那个简单多了= =),关键是线段树上域的更新,更新num的时候,注意,如果两个线段是连一起的,那么需要减1(可以用rb 和lb判断),因为这个len是当前的总长度,所以求线段长度的时候,就需要当前总长度减去更新前的总长度(的绝对值),也就是这次扫描线后的长度。

嗯。。大致也就理解这么多了,线段树变形太多了,呜呜。。。任重而道远!!

#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <cstring>
#include <string>
#include <algorithm>
#include <queue>
#include <stack>
#include <climits>
#define MID(x,y) ((x+y)>>1)
#define L(x) ( x << 1 )
#define R(x) ( x << 1 | 1 )
#define BUG printf("here!!!\n");

using namespace std;

const int MAX = 5010;
struct Tnode{ int l,r,num,len,cover;bool lb,rb;};
struct Sline{ int x,y1,y2,flag;};
Tnode node[MAX<<2];
Sline l[MAX<<1];
int y[MAX<<1];

void add_line(int x1,int y1,int x2,int y2,int &cnt)
{
	l[cnt].x = x1; l[cnt].y1 = y1; l[cnt].y2 = y2; l[cnt].flag = 1;
	y[cnt++] = y1;
	l[cnt].x = x2; l[cnt].y1 = y1; l[cnt].y2 = y2; l[cnt].flag = -1;
	y[cnt++] = y2;
}
void init()
{
	memset(node,0,sizeof(node));
}
void Build(int t,int l,int r)
{
	node[t].l = l; node[t].r = r;
	node[t].num = 0;
	if( l == r - 1 ) return ;
	int mid = MID(l,r);
	Build(R(t),mid,r);
	Build(L(t),l,mid);
}
void Updata_len(int t)
{
	if( node[t].cover > 0 )
	{
		node[t].num = node[t].lb = node[t].rb = 1;
		node[t].len = y[node[t].r] - y[node[t].l];
	}
	else
		if( node[t].l == node[t].r - 1 )
			node[t].rb = node[t].lb = node[t].len = node[t].num = 0;
		else
		{
			node[t].rb = node[R(t)].rb; node[t].lb = node[L(t)].lb;
			node[t].len = node[L(t)].len + node[R(t)].len;
			node[t].num = node[L(t)].num + node[R(t)].num - node[R(t)].lb*node[L(t)].rb;
		}		//两线段重合的话,得减一下。。 
}
void Updata(int t,Sline p)
{
	if( y[node[t].l] >= p.y1 && y[node[t].r] <= p.y2 )
	{
		node[t].cover += p.flag;
		Updata_len(t);
		return ;
	}
	if( node[t].l == node[t].r - 1 ) return ;
	int mid = MID(node[t].l ,node[t].r);
	if( p.y1 <= y[mid] ) Updata(L(t),p);
	if( p.y2 > y[mid] ) Updata(R(t),p);
	Updata_len(t);
}
int solve(int n,int cnt,Sline *l)
{
	init();
	Build(1,0,cnt-1);
	int ans = 0,last = 0,lines = 0;
	for(int i=0; i<n; i++)
	{
		Updata(1,l[i]);
		if( i >= 1 )
			ans += 2 * lines * (l[i].x - l[i-1].x);//计算平行于x轴的长度 
		ans += abs(node[1].len - last);		   // 计算平行于y轴的长度
		last = node[1].len; 
		lines = node[1].num;
	}
	return ans;
}

bool cmp(Sline a,Sline b)
{
	if( a.x == b.x ) return a.flag > b.flag;
	return a.x < b.x;
}
int main()
{
	int n,x1,x2,y1,y2;
	
	while( ~scanf("%d",&n) )
	{
		if( n == 0 )
		{
			printf("0\n");
			continue;
		} 
		int cnt = 0;
		for(int i=0; i<n; i++)
		{
			scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
			add_line(x1,y1,x2,y2,cnt);
		}
		sort(y,y+cnt);
		sort(l,l+cnt,cmp);
		int t = cnt;
		t = unique(y,y+cnt) - y;
		int ans = solve(cnt,t,l);
		printf("%d\n",ans);
	}

return 0;
}

抱歉!评论已关闭.