Problem
现在,二维平面上有N个点。实现以下三种操作:
1. 在点集里添加一个点;
2. 给出一个点,查询它到点集里所有点的曼哈顿距离的最小值;
3. 给出一个点,查询它到点集里所有点的曼哈顿距离的最大值。
Hint
1 ≤ N, Q ≤ 100,000,点的坐标是不超过10^8的非负整数。
Solution
首先考虑如何破掉曼哈顿距离。设当前要查询的点为(x,y),则可分四种情况讨论:
1.x<=x0&&y<=y0
(x0+y0)-(x+y)
2.x<=x0&&y>y0
(x0-y0)+(y-x)
3.x>x0&&y<=y0
(y0-x0)+(x-y)
4.x>x0&&y>y0
-(x0+y0)+(x+y)
设点的坐标和为xpy,坐标差为xmy,于是我们把曼哈顿距离拆成两个点的xpy/xmy的差。
我们先考虑不添加点时如何求答案。
鉴于点的坐标比较大,我们先把所有点的y坐标离散化。然后,把点集和询问丢进一个数组里,将其按x坐标为关键字从小到大排序。考虑开四颗线段树,分别维护minxpy、maxxpy、minxmy、maxxmy。首先顺序扫一遍数组,每次碰到一个点集中的点(x,y),我们就把它丢进四颗线段树中y的位置;每次碰到一个询问(x,y),则分类讨论:若是询问最小值,则根据上述四种情况,其答案与min(xpy-区间[1,y]的maxxpy,xmy-区间[y,n+q]的maxxmy)取min;若是询问最大值,则其答案与max(xpy-区间[1,y]的minxpy,xmy-区间[y,n+q]的minxmy)取max。扫完后清空线段树(注意,切忌直接memset,应要把原来点集中的点逐点清空,不然原本
(n为点数)的清空耗时就会膨胀为整个线段树的大小)。然后逆序扫一遍数组,碰到点集中的点同样丢进线段树中y的位置,碰到询问也同理。这样,我们便可在
的时间内求出不添加点时的答案。
那么考虑解决掉添加点带来的影响。
我们可以对时间CDQ分治。首先我们像打快排一样,设一个l、r,分别表示我们要做的区间的左右边界。设mid为区间中点,则此区间被分为左右两个区间。我们先递归做左右两个区间,然后再扫一遍左区间,记录下左区间所有的加点操作;扫一遍右区间,记录下右区间所有的询问。则将加点视为点集(因为我们记录的加点均在左区间,出现时间肯定比询问小),按上上段所述的方法更新一波右区间询问的答案。这也是CDQ分治与普通分治的差异之处:左区间会影响右区间。这样的话,我们就付出了时间的代价:将时间复杂度乘上了一个log,但却将问题变成了静态的。
时间复杂度:
。
Code
#include <cstdio>
#include <algorithm>
using namespace std;
#define N 100001
#define M 8*N
#define S 2147483647
#define ll long long
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fd(i,a,b) for(i=a;i>=b;i--)
int i,n,q,x;
struct point
{
ll x,y,xpy,xmy;
}a[N];
struct operation
{
ll t,x,y,xpy,xmy,ans;
}b[N];
struct T
{
ll x,y,num,xpy,xmy;
}c[M];
struct node
{
ll min,max;
}f[2][M];
bool compare1(T a,T b)
{
return a.y<b.y;
}
bool compare2(T a,T b)
{
return a.x<b.x;
}
void up(bool t,int v)
{
int a=v*2,b=a+1;
f[t][v].min=min(f[t][v].min,min(f[t][a].min,f[t][b].min));
f[t][v].max=max(f[t][v].max,max(f[t][a].max,f[t][b].max));
}
void insert(bool t,int v,int l,int r,int k,int p)
{
if(l==r)
{
f[t][v].min=f[t][v].max=p;
return;
}
int mid=(l+r)/2;
if(k<=mid)
insert(t,v*2,l,mid,k,p);
else insert(t,v*2+1,mid+1,r,k,p);
up(t,v);
}
void clear(int t,int v,int l,int r,int k)
{
f[t][v].max=-(f[t][v].min=S);
if(l==r)return;
int mid=(l+r)/2;
if(k<=mid)
clear(t,v*2,l,mid,k);
else clear(t,v*2+1,mid+1,r,k);
}
ll getmin(int t,int v,int l,int r,int x,int y)
{
if(l==x&&r==y)return f[t][v].min;
int mid=(l+r)/2;
ll ans=S;
if(x<=mid)ans=min(ans,getmin(t,v*2,l,mid,x,min(mid,y)));
if(y>mid)ans=min(ans,getmin(t,v*2+1,mid+1,r,max(mid+1,x),y));
return ans;
}
ll getmax(int t,int v,int l,int r,int x,int y)
{
if(l==x&&r==y)return f[t][v].max;
int mid=(l+r)/2;
ll ans=-S;
if(x<=mid)ans=max(ans,getmax(t,v*2,l,mid,x,min(mid,y)));
if(y>mid)ans=max(ans,getmax(t,v*2+1,mid+1,r,max(mid+1,x),y));
return ans;
}
void work(int as,int bs)
{
sort(c+1,c+as+bs+1,compare2);
int i,x;
fo(i,1,as+bs)
{
if((x=c[i].num)<=as)
{
insert(0,1,1,n+q,c[i].y,c[i].xpy);
insert(1,1,1,n+q,c[i].y,c[i].xmy);
continue;
}
if(!b[x-=as].t)continue;
b[x].ans=b[x].t==1?min(b[x].ans,min(b[x].xpy-getmax(0,1,1,n+q,1,b[x].y),
b[x].xmy-getmax(1,1,1,n+q,b[x].y,n+q))):
max(b[x].ans,max(b[x].xpy-getmin(0,1,1,n+q,1,b[x].y),
b[x].xmy-getmin(1,1,1,n+q,b[x].y,n+q)));
}
fo(i,1,as+bs)
if(c[i].num<=as)
{
clear(0,1,1,n+q,c[i].y);
clear(1,1,1,n+q,c[i].y);
}
fd(i,as+bs,1)
{
if((x=c[i].num)<=as)
{
insert(0,1,1,n+q,c[i].y,c[i].xpy);
insert(1,1,1,n+q,c[i].y,c[i].xmy);
continue;
}
if(!b[x-=as].t)continue;
b[x].ans=b[x].t==1?min(b[x].ans,min(getmin(1,1,1,n+q,1,b[x].y)-b[x].xmy,
getmin(0,1,1,n+q,b[x].y,n+q)-b[x].xpy)):
max(b[x].ans,max(getmax(1,1,1,n+q,1,b[x].y)-b[x].xmy,
getmax(0,1,1,n+q,b[x].y,n+q)-b[x].xpy));
}
fd(i,as+bs,1)
if(c[i].num<=as)
{
clear(0,1,1,n+q,c[i].y);
clear(1,1,1,n+q,c[i].y);
}
}
void cdq(int l,int r)
{
if(l==r)return;
int i,mid=(l+r)/2,as=0,bs=0;
cdq(l,mid);
cdq(mid+1,r);
fo(i,l,mid)
if(!b[i].t)
c[++as].x=b[i].x,c[as].y=b[i].y,c[as].num=as,c[as].xpy=b[i].xpy,c[as].xmy=b[i].xmy;
fo(i,mid+1,r)
if(b[i].t)
c[(++bs)+as].x=b[i].x,c[as+bs].y=b[i].y,c[as+bs].num=as+i;
if(as&&bs)work(as,bs);
}
int main()
{
scanf("%d",&n);
fo(i,1,n)
{
scanf("%d%d",&a[i].x,&a[i].y);
c[i].xpy=a[i].xpy=a[i].x+a[i].y;
c[i].xmy=a[i].xmy=a[i].x-a[i].y;
c[i].x=a[i].x;
c[i].y=a[i].y;
c[i].num=i;
}
scanf("%d",&q);
fo(i,1,q)
{
scanf("%d%d%d",&b[i].t,&b[i].x,&b[i].y);
b[i].xpy=b[i].x+b[i].y;
b[i].xmy=b[i].x-b[i].y;
b[i].ans=(b[i].t==1?1:-1)*S;
c[n+i].x=b[i].x;
c[n+i].y=b[i].y;
c[n+i].num=n+i;
}
sort(c+1,c+n+q+1,compare1);
fo(i,1,n+q)
if((x=c[i].num)<=n)
c[i].y=a[x].y=i;
else c[i].y=b[x-n].y=i;
fo(i,1,M-1)f[0][i].min=f[1][i].min=S,f[0][i].max=f[1][i].max=-S;
work(n,q);
cdq(1,q);
fo(i,1,q)
if(b[i].t)
printf("%lld\n",b[i].ans);
}