Vani原创 欢迎移步 OJ2648
但是这道题有一点麻烦:查找的结点四边八方都有点,答案可能出现在任意一个方向,不能无脑按x排序了
然后我们就想啊:要是当前结点为(x,y),其余的所有点都在(x,y)的左下角就好了
实际上我们是可以达到这种情况的
因为我们只关心距离,所以我们可以通过各种奇怪的对称:
关于x对称,关于y轴对称,关于原点对称
这样我们就把问题转化成了:对于一个点(x,y),计算其左下角的点中与ta曼哈顿距离最近的点
实际上:|x-x0|+|y-y0|=x-x0+y-y0=(x+y)-(x0+y0)
这样我们就可以用CDQ分治解决了
同样的,时间在输入的时候已经是有序的了
在分治的过程中,按照x坐标归并起来
最后把x+y的值扔进树状数组
口hu结束,我们看一下程序实现
for (int i=1;i<=n;i++)
{
scanf("%d%d",&po[i].x,&po[i].y); po[i].x++; po[i].y++;
po[i].type=1; po[i].num=i; po[tot].ans=M*3;
maxn=max(maxn,max(po[i].x,po[i].y));
}
m+=n;
for (int i=n+1;i<=m;i++)
{
scanf("%d%d%d",&po[i].type,&po[i].x,&po[i].y); po[i].x++; po[i].y++;
po[i].num=i; po[i].ans=N*3;
maxn=max(maxn,max(po[i].x,po[i].y));
}
sort(po+1,po+1+m,cmp);
maxn++;
CDQ(1,m);
for (int i=1;i<=m;i++)
a[i].x=-a[i].x+maxn; //转化后的坐标不能为负数,关于y轴对称
sort(po+1,po+1+m,cmp);
CDQ(1,m);
for (int i=1;i<=m;i++)
a[i].y=-a[i].y+maxn; //关于原点对称
sort(po+1,po+1+m,cmp);
CDQ(1,m);
for (int i=1;i<=m;i++)
a[i].x=-(a[i].x-maxn); //关于x轴对称
sort(po+1,po+1+m,cmp);
CDQ(1,m);
int t1=L,t2=M+1;
int maxx=0; //一开始我们按照x排序
for (int i=L;i<=R;i++) //现在按照时间分成前后两部分
if (po[i].num<=M) q[t1++]=po[i];
else q[t2++]=po[i];
树状数组不光能够计算前缀和,还可以计算前缀最大值
void clear(int x)
{
for (int i=x;i<=maxn;i+=(i&(-i))) t[i]=-1;
}
void change(int x,int z)
{
for (int i=x;i<=maxn;i+=(i&(-i)))
t[i]=max(t[i],z);
}
int askmax(int x)
{
int ans=-1;
for (int i=x;i>0;i-=(i&(-i))) ans=max(ans,t[i]);
return ans;
}
第一次写这道题的时候,用的是KDtree
之后在问到曲神KDtree的空间复杂度时,
曲神和DP 严肃地 告诉我说:CDQ比较好
//这里写代码片
#include
#include
#include
#include
using namespace std;
const int N=1000010;
const int M=3000010;
struct node{
int x,y,type,num,ans;
bool operator <(const node &a) const
{
if (x!=a.x) return xx;
else return typeq[N];
int n,m,tot=0,ans[N],totx=0,t[N],maxn=0;
int cmp1(const node &a,const node &b)
{
return a.numint cmp(const node &a,const node &b) //按照x坐标排序
{
if (a.x!=b.x) return a.xx;
else return a.typeint x)
{
for (int i=x;i<=maxn;i+=(i&(-i))) t[i]=-1;
}
void change(int x,int z)
{
for (int i=x;i<=maxn;i+=(i&(-i)))
t[i]=max(t[i],z);
}
int askmax(int x)
{
int ans=-1;
for (int i=x;i>0;i-=(i&(-i))) ans=max(ans,t[i]);
return ans;
}
void CDQ(int L,int R)
{
if (L==1&&R==m) memset(t,-1,sizeof(t));
if (L==R) return;
int M=(L+R)>>1;
int t1=L,t2=M+1;
int maxx=0; //一开始我们按照x排序
for (int i=L;i<=R;i++) //现在按照时间分成前后两部分
if (po[i].num<=M) q[t1++]=po[i];
else q[t2++]=po[i];
for (int i=L;i<=R;i++) po[i]=q[i];
t1=L,t2=M+1;
int last=0;
while (t2<=R) //处理询问
{
while (t1<=M&&po[t1].type==2) t1++;
while (t2<=R&&po[t2].type==1) t2++;
if (t1<=M&&po[t1].x<=po[t2].x)
{
change(po[t1].y,po[t1].x+po[t1].y);
last=t1++;
}
else if (t2<=R)
{
int t=askmax(po[t2].y);
if (t!=-1)
po[t2].ans=min(po[t2].ans,po[t2].x+po[t2].y-t);
t2++;
}
}
for (int i=L;i<=last;i++)
if (po[i].type==1)
clear(po[i].y);
CDQ(L,M); CDQ(M+1,R);
}
int main()
{
scanf("%d%d",&n,&m);
for (int i=1;i<=n;i++)
{
scanf("%d%d",&po[i].x,&po[i].y); po[i].x++; po[i].y++;
po[i].type=1; po[i].num=i; po[tot].ans=M*3;
maxn=max(maxn,max(po[i].x,po[i].y));
}
m+=n;
for (int i=n+1;i<=m;i++)
{
scanf("%d%d%d",&po[i].type,&po[i].x,&po[i].y); po[i].x++; po[i].y++;
po[i].num=i; po[i].ans=N*3;
maxn=max(maxn,max(po[i].x,po[i].y));
}
sort(po+1,po+1+m,cmp);
maxn++;
CDQ(1,m);
for (int i=1;i<=m;i++)
po[i].x=-po[i].x+maxn; //转化后的坐标不能为负数
sort(po+1,po+1+m,cmp);
CDQ(1,m);
for (int i=1;i<=m;i++)
po[i].y=-po[i].y+maxn;
sort(po+1,po+1+m,cmp);
CDQ(1,m);
for (int i=1;i<=m;i++)
po[i].x=-(po[i].x-maxn);
sort(po+1,po+1+m,cmp);
CDQ(1,m);
for (int i=1;i<=m;i++)
po[i].y=-(po[i].y-maxn);
sort(po+1,po+1+m,cmp1);
for (int i=1;i<=m;i++)
if (po[i].type==2)
printf("%d\n",po[i].ans);
return 0;
}