bzoj-1941 Hide and Seek

141 篇文章 0 订阅
88 篇文章 0 订阅

题意:

给出平面上n个点,求距离每个点最大距离减最小距离(不算自己)的最小值;

n<=500000;


题解:

今天写了两道KDTree然而另一道被常数卡飞。。所以就写这个不需要重构的题解吧;

虽说如此但是这毕竟还是裸题啊。。

KD树在实现的时候需要注意一些问题。。

就是每次到了下一个深度都要换一个不同的比较函数来进行排序建树;

否则似乎就变成和线段树/平衡树一样的东西了,那样剪枝的效果会大大下降;

c++的库函数中有nth_element这个神物来直接找到中间值,并分别将小于和大于的元素放在两边

有了这个似乎真是省去了不少麻烦呢。。

然后就是KD树的强剪枝方法,对于每个结点维护子树内所有点覆盖到的最大的K维几何体;

剪枝条件就是计算这个几何体离查询点的最大/最小距离,然后规划先搜索那个子树,减掉该剪的枝就可以了;

因为这道题没有插入所有还是很兹瓷的!时间复杂度?不知道哦!


代码:


#include<stdio.h>
#include<string.h>
#include<algorithm>
#define N 500100
using namespace std;
struct Point
{
	int x[2];
	friend int dis(Point a,Point b)
	{
		return abs(a.x[0]-b.x[0])+abs(a.x[1]-b.x[1]);
	}
}a[N],low[N],upp[N];
int ch[N][2];
int k,ban,af,ac;
bool cmp(Point a,Point b)
{
	if(a.x[k]!=b.x[k])
	return a.x[k]<b.x[k];
	return a.x[!k]<b.x[!k];
}
void Pushup(int rt)
{
	if(ch[rt][0])
	{
		low[rt].x[0]=min(low[rt].x[0],low[ch[rt][0]].x[0]);
		low[rt].x[1]=min(low[rt].x[1],low[ch[rt][0]].x[1]);
		upp[rt].x[0]=max(upp[rt].x[0],upp[ch[rt][0]].x[0]);
		upp[rt].x[1]=max(upp[rt].x[1],upp[ch[rt][0]].x[1]);
	}
	if(ch[rt][1])
	{
		low[rt].x[0]=min(low[rt].x[0],low[ch[rt][1]].x[0]);
		low[rt].x[1]=min(low[rt].x[1],low[ch[rt][1]].x[1]);
		upp[rt].x[0]=max(upp[rt].x[0],upp[ch[rt][1]].x[0]);
		upp[rt].x[1]=max(upp[rt].x[1],upp[ch[rt][1]].x[1]);
	}
}
int Build(int l,int r,int deep)
{
	if(l>r)	return 0;
	int mid=l+r>>1;
	k=deep&1;
	nth_element(a+l,a+mid,a+r+1,cmp);
	ch[mid][0]=Build(l,mid-1,deep+1);
	ch[mid][1]=Build(mid+1,r,deep+1);
	low[mid]=upp[mid]=a[mid];
	Pushup(mid);
	return mid;
}
int calf(int rt)
{
	return max(abs(low[rt].x[0]-a[ban].x[0]),abs(a[ban].x[0]-upp[rt].x[0]))+
		max(abs(low[rt].x[1]-a[ban].x[1]),abs(a[ban].x[1]-upp[rt].x[1]));
}
int calc(int rt)
{
	return max(low[rt].x[0]-a[ban].x[0],0)+max(a[ban].x[0]-upp[rt].x[0],0)+
		max(low[rt].x[1]-a[ban].x[1],0)+max(a[ban].x[1]-upp[rt].x[1],0);
}
void queryf(int rt)
{
	if(!rt)	return ;
	af=max(af,dis(a[ban],a[rt]));
	int l=calf(ch[rt][0]),r=calf(ch[rt][1]);
	if(l>r)
	{
		if(l>af)	queryf(ch[rt][0]);
		if(r>af)	queryf(ch[rt][1]);
	}
	else
	{
		if(r>af)	queryf(ch[rt][1]);
		if(l>af)	queryf(ch[rt][0]);
	}
}
void queryc(int rt)
{
	if(!rt)	return ;
	if(rt!=ban)
		ac=min(ac,dis(a[ban],a[rt]));
	int l=calc(ch[rt][0]),r=calc(ch[rt][1]);
	if(l<r)
	{
		if(l<ac)	queryc(ch[rt][0]);
		if(r<ac)	queryc(ch[rt][1]);
	}
	else
	{
		if(r<ac)	queryc(ch[rt][1]);
		if(l<ac)	queryc(ch[rt][0]);
	}
}
int main()
{
	int n,m,i,j,k,x,y,rt,ans;
	scanf("%d",&n);
	for(i=1;i<=n;i++)
		scanf("%d%d",a[i].x,a[i].x+1);
	rt=Build(1,n,0);
	ans=0x3f3f3f3f;
	for(i=1;i<=n;i++)
	{
		ban=i;
		af=0;
		ac=0x3f3f3f3f;
		queryf(rt);
		queryc(rt);
		ans=min(ans,af-ac);
	}
	printf("%d\n",ans);
	return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值