您的位置:首页 > 其它

hiho一下 第124周 #1421 : 四叉树 【二维线段树】

2016-11-20 20:50 489 查看


#1421 : 四叉树

时间限制:20000ms
单点时限:2000ms
内存限制:256MB


描述

小Ho:下个周末我们打算去隔壁城市玩吧?
小Hi:反正来回也挺近的,好啊。
小Ho:那么我先来规划一下游玩路线吧。
小Ho打开了手机中的地图APP,把坐标移动到了隔壁的城市。各种各样的店铺显示在了街道的地图上。
小Hi:小Ho,你知道地图APP是怎么计算出你周围的店铺么?
小Ho:哎?没有想过哎。
小Hi:其实这也是个很有意思的问题呢。我们先把模型简化一下,假设有一张平面图,上面分布了N个点,第i个点的坐标为(x[i],y[i])。
小Ho:用点来代表店铺么?
小Hi:是的,然后假如我们所在的坐标为(a,b),那么以我们为中心半径为r的范围内(包含边上),有多少个点呢?
小Ho:感觉是个很有意思的问题呢,让我想一想。
提示:四叉树(Quadtree)


输入

第1行:2个整数N,M。1≤N≤50,000,0≤M≤5,000。
第2..N+1行:每行2个整数x,y,第i+1行表示第i个点的坐标,保证没有重复的点。0≤x,y≤30,000
第N+2..N+M+1行:每行3个整数a,b,r,表示询问的中心坐标(a,b),以及半径r。0≤a,b,r≤30,000


输出

第1..M行:每行1个整数,第i行表示以第i个询问的(x,y)为中心所包含的点数。

样例输入
2 2
1 1
2 2
2 2 1
2 2 2


样例输出
1
2



提示:四叉树(Quadtree)

小Ho:朴素的想法是我用一个二维数组来把整个平面图表示出来。假设坐标的范围是L,那么就需要一个L*L的数组。
对于(a,b)和r,我就检查a-r到a+r行的b-r列到b+r列,看其中是否存在有点,并且点到(a,b)的距离是小于等于r的。
对于L超过10000的情况就没有办法实现了。
小Hi:没错,在坐标范围和点数都很大的情况下,确实会有这样的问题。
小Ho:我在想能不能把整个区域分割成若干的小区域,每次都在附近的小区域去找临近的点呢。
小Hi:小Ho你这个想法很棒,我们不妨来试试吧?
小Ho:那应该怎么分割呢?
小Hi:你还记得线段树么?在线段树的处理中,我们将一个区间从中点分成两段。这里我们也用同样的方法,将一个区域分割为4块好了:



从上到下,从左到右,分别标记为1234。
我们将所有的点放进这些区域中,为了让一个区域中点数不过多,我们设定一个区域的点数上限。
若一个区域的点数过多,我们就将这个区域四分,把新的点放到子区域中去。
小Ho:听上去好像很有道理。
小Hi:当然有道理了,这种数据结构叫做"四叉树(Quadtree)"。其每个基本单元为:
QuadtreeNode:
const NODE_CAPACITY; // 每个节点包含的点数限制,常量
boundary; // 该节点的范围,包含4个参数,区域的上下左右边界
points; // 该区域内节点的列表
childNode; // 包含4个参数,分别表示4个子区域

假设NODE_CAPACITY=1,那么我们可以把整个区域分割为:



小Ho:恩,这个我理解了,因为跟线段树差不多,那么也就是同样存在插入和查询操作了?
小Hi:没错。
四叉树的插入操作:将新的节点(x,y)插入时,若不在当前区域内,退出;否则将其加入该区域的节点列表points,若当前区域的节点列表已经满了。那么将该区域进行四分,同时将节点加入子区域中。
insert(QuadtreeNode nowNode, point p):
If (p not in nowNode.boundary) Then
Return
End If
If (nowNode.points.length < NODE_CAPACITY) Then
nowNode.points.append(p)
Else
nowNode.divide() // 将区域四分
For each childNode of nowNode
insert(childNode, p)
End For
End If

四叉树的查询操作一般是求一个范围内的点,因此带入的参数也是一个区域range:
query(QuadtreeNode nowNode, range):
If (QuadtreeNode.boundary does not intersect range) Then
//该节点的区域与查询区域不相交
Return empty
End If
For each p in nowNode.points
If (p in range) Then
pointsInRange.append(p)
End For
End For
If (nowNode.isDivide) Then
// 如果该区域有分割过,那么子区域中的节点也有可能在其中
For each childNode of nowNode
query(childNode, range)
End For
End If
Return pointsInRange

在我们这次的问题中,我们可以用圆的外接正方形作为区域来求得点的列表,再以此检查是否在圆内。
小Ho:这样看上去的确搜索量变少了。
小Hi:没错,而且动态建立四叉树的过程也减少了空间的开销。
小Ho:恩,我来实现一下试试!

好尴尬-.-在插入点时迷了,迷了,迷了呀---呜呜-.-

代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int xx[60000],yy[60000];
struct node{
bool fafe;
int length,hao[5],x[2],y[2];
node * clidren[4];
}pp[1000000];
void lon(node *x)
{
x->length=0;
x->fafe=false;
}
int a,b,r,ans,lp,ll;
void pan(int i)//判断是否在圆中
{
if ((a-xx[i])*(a-xx[i])+(b-yy[i])*(b-yy[i])<=r*r)
ans++;
}
void add(int ii,node * x);
void fen(node * x)
{
int mx=(x->x[0]+x->x[1])>>1;
int my=(x->y[0]+x->y[1])>>1;
for (int i=0;i<4;i++)
x->clidren[i]=&pp[lp++];//一分为四
x->clidren[0]->x[0]=x->x[0];x->clidren[0]->x[1]=mx;
x->clidren[0]->y[0]=x->y[0];x->clidren[0]->y[1]=my;
x->clidren[1]->x[0]=mx+1;x->clidren[1]->x[1]=x->x[1];
x->clidren[1]->y[0]=x->y[0];x->clidren[1]->y[1]=my;
x->clidren[2]->x[0]=x->x[0];x->clidren[2]->x[1]=mx;
x->clidren[2]->y[0]=my+1;x->clidren[2]->y[1]=x->y[1];
x->clidren[3]->x[0]=mx+1;x->clidren[3]->x[1]=x->x[1];
x->clidren[3]->y[0]=my+1;x->clidren[3]->y[1]=x->y[1];
//没错
for (int i=0;i<4;i++)
lon(x->clidren[i]);
//没错
for (int j=0;j< x->length ;j++)
{
int ii=x->hao[j];
for (int i=0;i<4;i++)
{
if (x->clidren[i]->x[0]<=xx[ii]&&x->clidren[i]->x[1]>=xx[ii]&&x->clidren[i]->y[0]<=yy[ii]&&x->clidren[i]->y[1]>=yy[ii])
{
add(ii,x->clidren[i]);
break;
}
}
}
//没错
x->fafe=true;
x->length=0;
//没错
}
void add(int ii,node * x)//插入点
{
if (!x->fafe)
{
x->hao[x->length++]=ii;
if (x->length>ll)
{
fen(x);
}
}
else
{
for (int i=0;i<4;i++)
{
if (x->clidren[i]->x[0]<=xx[ii]&&x->clidren[i]->x[1]>=xx[ii]&&x->clidren[i]->y[0]<=yy[ii]&&x->clidren[i]->y[1]>=yy[ii])
{
add(ii,x->clidren[i]);
/*x->clidren[i]->hao[x->clidren[i]->length++]=ii;--开始就是这样错的---这个区域可能已经分了呀-.-
if (x->clidren[i]->length>ll)
{
fen(x->clidren[i]);
}*/
break;
}
}
}
}
void query(node * x,int l,int r,int xi,int s)//查找
{
if (!x->fafe)
{
if (x->length)
{
for (int i=0;i<x->length;i++)
pan(x->hao[i]);
}
return ;
}
for (int i=0;i<4;i++)
{
if (l>=x->clidren[i]->x[0]&&r<=x->clidren[i]->x[1]&&xi>=x->clidren[i]->y[0]&&s<=x->clidren[i]->y[1])
{
query(x->clidren[i],l,r,xi,s);
return ;
}
}
if (l>=x->clidren[1]->x[0])
{
query(x->clidren[1],l,r,xi,x->clidren[1]->y[1]);
query(x->clidren[3],l,r,x->clidren[3]->y[0],s);
}
else if (r<=x->clidren[0]->x[1])
{
query(x->clidren[0],l,r,xi,x->clidren[0]->y[1]);
query(x->clidren[2],l,r,x->clidren[2]->y[0],s);
}
else if (xi>=x->clidren[2]->y[0])
{
query(x->clidren[2],l,x->clidren[2]->x[1],xi,s);
query(x->clidren[3],x->clidren[3]->x[0],r,xi,s);
}
else if (s<=x->clidren[0]->y[1])
{
query(x->clidren[0],l,x->clidren[0]->x[1],xi,s);
query(x->clidren[1],x->clidren[1]->x[0],r,xi,s);
}
else
{
query(x->clidren[0],l,x->clidren[0]->x[1],xi,x->clidren[0]->y[1]);
query(x->clidren[1],x->clidren[1]->x[0],r,xi,x->clidren[1]->y[1]);
query(x->clidren[2],l,x->clidren[2]->x[1],x->clidren[2]->y[0],s);
query(x->clidren[3],x->clidren[3]->x[0],r,x->clidren[3]->y[0],s);
}
}
int main()
{
int n,m;
scanf("%d%d",&n,&m);
pp[0].x[0]=0;pp[0].x[1]=30000;
pp[0].y[0]=0;pp[0].y[1]=30000;
lon(&pp[0]);lp=1;
ll=4;
for (int i=0;i<n;i++)
{
scanf("%d%d",&xx[i],&yy[i]);
add(i,&pp[0]);
}
for (int i=0;i<m;i++)
{
scanf("%d%d%d",&a,&b,&r);
ans=0;
query(&pp[0],max(0,a-r),min(30000,a+r),max(0,b-r),min(30000,b+r));
printf("%d\n",ans);
}
return 0;
}
/*
2 3

0 1
*/
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: