第一次接触线段树,感觉确实很复杂,看了很久
一些关于线段树的介绍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;
}