poj 1151 亚特兰蒂斯

第一次接触线段树,感觉确实很复杂,看了很久

一些关于线段树的介绍http://dongxicheng.org/structure/segment-tree/

主要参考了这个解题报告

线段树结构中,每个节点所表示的key值含义是一个很重要的东西,如何赋予它具体的含义,以及更新和查询,

感觉是一个重点问题

下面这个是转的http://blog.sina.com.cn/s/blog_77dc9e0801018v2z.html

#include <cstdio>
#include <iostream>
#include <cstring>
#include <cstdlib>
using namespace std;                                              
///
///
#define INF 20000000
#define MAX 10010
#define max(a,b)(a>b?a:b)
///
int n;
double x1, x2, y1, y2;
int num;
double y[MAX];
struct Line{    
	double x, y1, y2;    
	int flag;
}lline[MAX];

struct node{    
	int left, right;    
	double l, r, len;    
	int flag;
}pt[MAX];

int cmp1(const void *a, const void *b)
{    
	if(*(double *)a > *(double *)b) 
		return 1;    
	else 
		return -1;
}

int cmp2(const void *a, const void *b)
{    
    if( (*(Line *)a).x > (*(Line *)b).x ) return 1;    
	else return -1;
}  


void buildTree(int l, int r, int pos)
{    
	pt[pos].left = l, pt[pos].right = r;    
	pt[pos].l = y[l], pt[pos].r = y[r];    
	pt[pos].len = pt[pos].flag = 0;        
	if( (l+1) == r ) 
		return ;    
	int mid = (l+r)/2;    
	buildTree(l, mid, pos*2);    
	buildTree(mid, r, pos*2+1);
}
void insertLength(int pos)
{    
	if(pt[pos].flag > 0)        
		pt[pos].len = (pt[pos].r - pt[pos].l);    
	else if( (pt[pos].left+1) == pt[pos].right )        
		pt[pos].len = 0;    
	else        
		pt[pos].len = (pt[pos*2].len + pt[pos*2+1].len);
} 
void insert(Line e, int pos)
{    
	if(pt[pos].l == e.y1 && pt[pos].r == e.y2)    
	{        
		pt[pos].flag += e.flag;        
		insertLength(pos);        
		return ;    
	}        
	if(e.y1 >= pt[pos*2 + 1].l)        
		insert(e, pos*2 + 1);    
	else if(e.y2 <= pt[pos * 2].r)        
		insert(e, pos * 2);    
	else    
	{        
		Line temp = e;        
		temp.y2 = pt[pos * 2].r;        
		insert(temp, pos * 2);                
		temp = e;        
		temp.y1 = pt[pos * 2 + 1].l;        
		insert(temp, pos * 2 + 1);    
	}    
	insertLength(pos);
}
int main()
{
	freopen("F:\\input.txt","r",stdin );
	///
	int i, j;
	int caseNum = 1;
	while (scanf("%d", &n) != EOF)
	{
		if (n == 0)
			break;
		num = 1;
		for (i = 0; i < n; i++)
		{
			scanf("%lf %lf %lf %lf", &x1, &y1, &x2, &y2);
			lline[num].x = x1;
			lline[num].y1 = y1;
			lline[num].y2 = y2;
			lline[num].flag = 1;
			y[num++] = y1;

			lline[num].x = x2;
			lline[num].y1 = y1;
			lline[num].y2 = y2;
			lline[num].flag = -1;
			y[num++] = y2; 
		}
		qsort(y + 1, num - 1, sizeof(y[1]), cmp1);        
		qsort(lline + 1, num - 1, sizeof(lline[1]), cmp2);
		buildTree(1, num - 1, 1);

		insert(lline[1], 1);
		double result = 0;        
		for(i = 2; i < num; ++i)        
		{            
			result += pt[1].len * (lline[i].x - lline[i - 1].x);             
			insert(lline[i], 1);        
		}        
		printf("Test case #%d\n",caseNum++);        
		printf("Total explored area: %.2f\n\n",result);   
//		printf("\n");
	}
	
	///
    return 0;
}                                              



模仿写了一个,采用指针的方式

注意sort的参数值是左闭区间,右开区间

 对于Y坐标进行区间处理建立“基于Y坐标的”线段树

注意:线段树除了最后一层外,每一层都是满的,也即是说要么是叶子,要么两个孩子都有

线段树结构中的key值是要根据具体问题来设定,比如

本题中:len表示:当前区间下有效y长度的值,每一次插入垂直于x的线段以后,更新该值,

树根的含义则为,当前情况下总区间内有效区间长度,该长度乘以【下一个线段x坐标 - 当前线段x坐标】即为该段x坐标下

距离,依次向右推进,逐步计算,得出所有的面积。不会出现重复计算,因为每次插入都更新了叶子结点中

有效长度的情况,int flag就是为了来表示当前叶子的有效长度的标记,如果为正那么当前长度有效。

之前像用bool flag来表示,无法通过,还是按照叠加值来表示

#include <iostream>
#include <sstream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <string>
#include <vector>
#include <set>
#include <cctype>
#include <algorithm>
#include <cmath>
#include <deque>
#include <queue>
#include <map>
#include <queue>
#include <list>
#include <iomanip>
using namespace std;                                               
///
///
#define maxn 1005
double x1pos, y1pos, x2pos, y2pos;
//线段树的结点结构
struct Node
{
	double ld, rd;//左右区间标记
	Node *lc, *rc;//左右孩子
	double len;//该区间的有效长度
	int flag;//标记重复
	Node()
	{
		ld = rd = 0;
		lc = rc = NULL;
		len = 0;
		flag = 0;
	}
}Segment_tree[maxn];
struct Line{
	double x, y_1, y_2;
	int flag;
	Line()
	{
		x = y_1 = y_2 = 0;
		flag = 0;
	}
}lines[maxn];
bool operator < (Line l1, Line l2)
{
	return l1.x < l2.x;
}
double ypos[maxn];//暂存y坐标
int n;
Node *buildtree(int a,int b)
{ 
    Node * p = new Node;//给P申请一块内存 
    p->ld = ypos[a]; 
    p->rd = ypos[b]; 
	p->len = 0;
    //{初始化 p->key } 
    if(abs(a - b) == 1)
	{
		p->lc = p->rc = NULL;
		return p;//叶子节点 
	}
    p->lc = buildtree(a, (a + b)/2); 
    p->rc = buildtree((a + b)/2, b); 
    return p; 
} 
void update(Node *node)
{
	if (node->flag > 0)
		node->len = node->rd - node->ld;
	else if (node->lc == NULL && node->rc == NULL)
		node->len = 0;
	else
		node->len = node->lc->len + node->rc->len;
}
void insert_sg_tree(Node *T, Line edge)
{
	int i, j;
	//如果相等刚好覆盖区间
	if (edge.y_1 == T->ld && edge.y_2 == T->rd)
	{
		T->flag += edge.flag;
		update(T);
		return;
	}
	//如果小于左子区间
	if (edge.y_2 <= T->lc->rd)
		insert_sg_tree(T->lc, edge);
	//如果大于右子区间边界
	else if (edge.y_1 >= T->rc->ld)
		insert_sg_tree(T->rc, edge);
	else
	{
		Line tempLine;
		tempLine = edge;
		tempLine.y_2 = T->lc->rd;
		insert_sg_tree(T->lc, tempLine);
		tempLine = edge;
		tempLine.y_1 = T->rc->ld;
		insert_sg_tree(T->rc, tempLine);
	}
	update(T);
}
int main()
{
	///
	int i, j, k;
	int cases = 1;
	while (scanf("%d", &n) != EOF)
	{
		if (n == 0)
			break;
		int num = 0;
		for (i = 0; i < n; i++)
		{
			scanf("%lf %lf %lf %lf", &x1pos, &y1pos, &x2pos, &y2pos);
			lines[num].x = x1pos;
			lines[num].y_1 = y1pos;
			lines[num].y_2 = y2pos;
			lines[num].flag = 1;
			ypos[num++] = y1pos;

			lines[num].x = x2pos;
			lines[num].y_1 = y1pos;
			lines[num].y_2 = y2pos;
			lines[num].flag = -1;
			ypos[num++] = y2pos;
		}
		//对y坐标进行排序
		sort(ypos, ypos + num);
		//对即将插入的直线数组进行排序
		sort(lines, lines + num);

		//对于y坐标构建区间树
		Node *Tree = NULL;
		Tree = buildtree(0, num - 1);
		//每插入一次垂直于x轴的线段,更新当前线段树中的len关键数值
		double ans = 0;
		insert_sg_tree(Tree, lines[0]);
		for (i = 1; i < num; i++)
		{
			ans += Tree->len * (lines[i].x - lines[i - 1].x);
			insert_sg_tree(Tree, lines[i]);
		}
		printf("Test case #%d\n", cases++);
		printf("Total explored area: %.2f\n", ans);
		printf("\n");

	}

	///
    return 0;
}                                              



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值