您的位置:首页 > 其它

POJ 1177 Pictures(HDU 1828) (线段树+离散化+线段扫描)

2016-08-22 18:08 369 查看
转载来自:点击打开链接

对于这题,我们的思路步骤如下(代码和下面的文字解释结合着看):

1.对于输入的N个矩形,有2*N条纵向边,我们把这些边叫做扫描线

2.建立一个struct ScanLine,保留这些扫描线
struct ScanLine
{
int x;//横坐标
int y1;//扫描线的下端点
int y2;//扫描线的上端点
int flag;//若该扫描线属于矩形的左边的竖边,如AB,则叫做入边,值为1,若属于矩形的右边的竖边,如CD,则叫做出边,值为0
};


3.建立数组struct ScanLine scan[LEN];保存输入值,同时用y[LEN]保存所有的纵向坐标

4.对scan数组进行排序,即所有竖边从左往右排序;对y排序并去除重复值,然后离散化,建立线段树。(PS:线段树的node[i].left和node[i].right保存的都是离散化的值,y[node[i].left]和y[node[i].right]保存的就是实际值,这个在代码中很容易理解)

线段树节点struct Node
struct Node
{
int left;
int right;
int count;//被覆盖次数
int line;//所包含的区间数量,如三条[1,2],[2,3],[4,5]线段被覆盖,则line=2,因为 [1,2],[2,3]是连续的。这个是用来辅助计算横边的,如图,在AB和EG之间的横边AK和BL,它们是边界,line=1,|AB|+|EG|=2*line*|AB|
int lbd;//左端点是否被覆盖,用来辅助对line的计算
int rbd;//右端点是否被覆盖,用来辅助对line的计算
int m;//测度,即覆盖的区间长度,如[2,8]就为6
};


好的,上面建立了大的框架,然后就开始扫描了。

1.将排序后的scan数组依次输入,执行插入线段insert函数(为入边)或者remove函数(为出边),同时更新m和line

2.没扫描一次,就要计算一次周长perimeter,这里我们以图中的例子来讲解过程:

   首先是AB,它被插入线段树,perimeter = perimeter + |AB|;

   然后是EG,它被插入线段树,此时线段树的root节点的测度为|EG|的值,但由于之前之前加过|AB|,因而应该减去|AB|,其实就是减去|KL|,然后再加上line*2*|AK|,这里的line的值是未插入EG时线段树的根节点的line值。
在原作者的代码上稍微做了点优化,去掉了一些不必要的语句:

//
// main.cpp
// Richard
//
// Created by 邵金杰 on 16/8/22.
// Copyright © 2016年 邵金杰. All rights reserved.
//

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
using namespace std;
const int maxn=5000+100;
struct line{
int x,y1,y2;
int is_left;
}line[maxn*2];
struct node{
int l,r;
int count;
int line,lbd,rbd;
int len;
}tree[maxn*4];
int y[maxn*2];
bool cmp(struct line l1,struct line l2)
{
if(l1.x==l2.x) return l1.is_left>l2.is_left;
return l1.x<l2.x;
}
int getmid(int l,int r)
{
return (l+r)/2;
}
void BuildTree(int p,int l,int r)
{
tree[p].l=l;
tree[p].r=r;
tree[p].count=0;
tree[p].line=tree[p].lbd=tree[p].rbd=0;
tree[p].len=0;
if(r-l>1){
int mid=getmid(l,r);
BuildTree(p*2+1,l,mid);
BuildTree(p*2+2,mid,r);
}
}
void updata_len(int p)
{
if(tree[p].count>0)
tree[p].len=y[tree[p].r]-y[tree[p].l];
else if(tree[p].r-tree[p].l==1)
tree[p].len=0;
else
tree[p].len=tree[p*2+1].len+tree[p*2+2].len;
}
void updata_line(int p)
{
if(tree[p].count>0)
tree[p].line=tree[p].lbd=tree[p].rbd=1;
else if(tree[p].r-tree[p].l==1)
tree[p].line=tree[p].lbd=tree[p].rbd=0;
else{
tree[p].lbd=tree[p*2+1].lbd;
tree[p].rbd=tree[p*2+2].rbd;
tree[p].line=tree[p*2+1].line+tree[p*2+2].line-tree[p*2+1].rbd*tree[p*2+2].lbd;
}
}
void Insert(int p,int l,int r)
{
if(y[tree[p].l]==l&&y[tree[p].r]==r)
tree[p].count++;
else{
int mid=getmid(tree[p].l,tree[p].r);
if(r<=y[mid])
Insert(p*2+1,l,r);
else if(l>=y[mid])
Insert(p*2+2,l,r);
else{
Insert(p*2+1,l,y[mid]);
Insert(p*2+2,y[mid],r);
}
}
updata_len(p);
updata_line(p);
}
void Delete(int p,int l,int r)
{
if(y[tree[p].l]==l&&y[tree[p].r]==r)
tree[p].count--;
else{
int mid=getmid(tree[p].l,tree[p].r);
if(r<=y[mid])
Delete(p*2+1,l,r);
else if(l>=y[mid])
Delete(p*2+2,l,r);
else{
Delete(p*2+1,l,y[mid]);
Delete(p*2+2,y[mid],r);
}
}
updata_len(p);
updata_line(p);
}
int main()
{
int n;
while(scanf("%d",&n)!=EOF)
{
int yc=0;
int x1,y1,x2,y2;
for(int i=0;i<n;i++)
{
scanf("%d %d %d %d",&x1,&y1,&x2,&y2);
line[yc].x=x1;
line[yc].y1=y1;
line[yc].y2=y2;
line[yc].is_left=1;
y[yc++]=y1;
line[yc].x=x2;
line[yc].y1=y1;
line[yc].y2=y2;
line[yc].is_left=0;
y[yc++]=y2;
}
sort(y,y+yc);
int lc=(int)(unique(y,y+yc)-y);
sort(line,line+yc,cmp);
BuildTree(0,0,lc-1);
int l=0,len=0,c=0;
for(int i=0;i<yc;i++)
{
if(line[i].is_left)
Insert(0,line[i].y1,line[i].y2);
else
Delete(0,line[i].y1,line[i].y2);
if(i>=1)
c+=2*l*(line[i].x-line[i-1].x);
c+=abs(tree[0].len-len);
len=tree[0].len;
l=tree[0].line;
}
printf("%d\n",c);
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: