题目传送门
一道cdq分治好题
我们首先考虑离线下来做,对于一次询问操作点 ( x , y ) (x,y) (x,y) 以及要找与其配对得最近点 ( x ′ , y ′ ) (x',y') (x′,y′),其间的距离 D = ∣ x − x ′ ∣ + ∣ y − y ′ ∣ D=|x-x'|+|y-y'| D=∣x−x′∣+∣y−y′∣ 很显然带着绝对值不好处理,我们考虑去掉。
于是我们想到了一个有效的方法,即假设 ( x ′ , y ′ ) (x',y') (x′,y′) 都在 ( x , y ) (x,y) (x,y) 的左下角,即满足 x ′ ≤ x , y ′ ≤ y x'\leq x,y'\leq y x′≤x,y′≤y 然后要使得 x + y − ( x ′ + y ′ ) x+y-(x'+y') x+y−(x′+y′) 最小即要求 x ′ + y ′ x'+y' x′+y′最大。那么就是经典三维偏序问题,我们可以用cdq来解决。
那么我们分别对于询问点在器左上,左下,右上,右下分别做一遍即可。
时间复杂度 O ( 4 × n × log 2 max ( x i , y i ) ) O(4\times n\times\log^2 \max(x_i,y_i)) O(4×n×log2max(xi,yi))
我们为了不被卡常需要把cdq里面的排序换成归并来处理即可。
const int M=1e7+5;
const int N=1e6+5;
int n,m,mx,C[M];
struct Node
{
int type,x,y,id,ans;
};
Node a[N],b[N],c[N];
inline void Add(int x,int v)
{
if(x<=0) return;
while(x<=mx)
{
C[x]=max(C[x],v);
x+=lowbit(x);
}
}
inline int Ask(int x)
{
if(x<=0) return 0;
int ans=0;
while(x)
{
ans=max(ans,C[x]);
x-=lowbit(x);
}
return (ans)?ans:-1000000000;
}
inline void Clear(int x)
{
if(x<=0) return;
while(x<=mx)
{
C[x]=0;
x+=lowbit(x);
}
}
inline void cdq(int l,int r)
{
if(l>=r) return;
int mid=l+r>>1;
cdq(l,mid); cdq(mid+1,r);
int L=l,R=mid+1;
int gs=l;
while(R<=r)
{
while(L<=mid&&b[L].x<=b[R].x)
{
if(b[L].type==1) Add(b[L].y,b[L].x+b[L].y);
c[gs++]=b[L++];
}
if(b[R].type==2) a[b[R].id].ans=min(a[b[R].id].ans,b[R].x+b[R].y-Ask(b[R].y));
c[gs++]=b[R++];
}
For(k,l,L-1) if(b[k].type==1) Clear(b[k].y);
while(L<=mid) c[gs++]=b[L++];
For(i,l,r) b[i]=c[i];
}
inline void solve(int opx,int opy)
{
For(i,1,n+m)
{
b[i]=a[i];
if(opx) b[i].x=mx-b[i].x;
if(opy) b[i].y=mx-b[i].y;
}
cdq(1,n+m);
}
int main()
{
io.read(n),io.read(m);
For(i,1,n)
{
int x,y;
io.read(x),io.read(y);
a[i].type=1;
a[i].x=x+1,a[i].y=y+1;
mx=max(max(x+1,y+1),mx);
a[i].id=i;
}
For(i,n+1,n+m)
{
int x,y,type;
io.read(type),io.read(x),io.read(y);
a[i].type=type;
a[i].x=x+1,a[i].y=y+1;
a[i].ans=1e9;
mx=max(max(x+1,y+1),mx);
a[i].id=i;
}
mx++;
For(i,0,1) For(j,0,1) solve(i,j);
int ask=0;
For(i,n+1,n+m) if(a[i].type==2)
io.write(a[i].ans),putchar('\n');
return 0;
}