题目描述
Ayu 在七年前曾经收到过一个天使玩偶,当时她把它当作时间囊埋在了地下。而七年后 的今天,Ayu 却忘了她把天使玩偶埋在了哪里,所以她决定仅凭一点模糊的记忆来寻找它。
我们把 Ayu 生活的小镇看作一个二维平面坐标系,而 Ayu 会不定时地记起可能在某个点 (xmy) 埋下了天使玩偶;或者 Ayu 会询问你,假如她在 (x,y) ,那么她离近的天使玩偶可能埋下的地方有多远。
因为 Ayu 只会沿着平行坐标轴的方向来行动,所以在这个问题里我们定义两个点之间的距离为dist(A,B)=|Ax-Bx|+|Ay-By|。其中 Ax 表示点 A的横坐标,其余类似。
Sol
CDQ 分治太难写了 , 蒟蒻不会 , 只能打个 KD_Tree 骗分…
如果写 KD_Tree 的话 , 就直接是求解最近邻问题了 , 和原来的平面最近点对一样 , 只是把欧式距离改为了曼哈顿距离
简单的思路:
建出 KD_Tree 后 , 对于每一组询问直接在 KD_Tree 上不停地向左右子树递归寻找最优解,
利用 KD_Tree 记录的当前节点统辖的点的上下边界来算出可能的最优答案并进行剪枝 , 因为每次询问只有一个点 , 我们 KD_Tree 良好的划分方式使得剪枝效率还是比较高的
插入操作就直接插进去就行了
如果是 bzoj ,这样写就过了 , 但是在洛谷上是过不了的 , 因为直接插入会使 KD_Tree 很不平衡
所以要用到重构的思想来保证复杂度
一种方法是每插入个 5000~10000 次就直接把整棵树重构一次,这种方法比较省力
但是还是可能被卡 , 有趣的是如果在 bzoj 上这样写重构也会 TLE , 但是不重构就过了…
而洛谷上的数据比较强 , 这种重构方法貌似过不了 , 这时要用到替罪羊树的思想
如果某一棵子树的左右儿子相差过大 , 那么就把这棵子树重构, 这样重构的好处在于有目的性 , 重构的地方精准 , 能很大程度上提升效率
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<queue>
#include<algorithm>
#include<set>
#define Set(a,b) memset(a,b,sizeof(a))
using namespace std;
namespace IO{
const int maxn(1<<21|1);
char ibuf[maxn],*iS,*iT,obuf[maxn],*oS=obuf,*oT=obuf+maxn-1,c,st[55];
int f,tp;
char Getc(){
return (iS==iT?(iT=(iS=ibuf)+fread(ibuf,1,maxn,stdin),(iS==iT?EOF:*iS++)):*iS++);
}
void Flush(){
fwrite(obuf,1,oS-obuf,stdout);
oS=obuf;
}
void Putc(char x){
*oS++=x;
if(oS==oT)Flush();
}
template<class Int>void Input(Int &x){
for(f=1,c=Getc();c<'0'||c>'9';c=Getc())f=c=='-'?-1:1;
for(x=0;c<='9'&&c>='0';c=Getc())x=(x<<3)+(x<<1)+(c^48);
x*=f;
}
template<class Int>void Print(Int x){
if(!x)Putc('0');
if(x<0)Putc('-'),x=-x;
while(x)st[++tp]=x%10+'0',x/=10;
while(tp)Putc(st[tp--]);
}
}
using IO::Input;
using IO::Print;
using IO::Putc;
#define LS T[u].ls
#define RS T[u].rs
namespace KD_tree{
typedef long long ll;
typedef double db;
const int N=6e5+10;int ti=0;
const int INF=1e9;
const db alpha=0.75;
int rt,cnt,now;int ans;
int n,m;
struct point {
int data[2];
inline bool operator <(point b)const{
return data[now]<b.data[now];
}
}a[N];
int st[N],top=0;
struct node{
int ls,rs,data[2];int Min[2],Max[2];int size;
node(){ls=rs=data[1]=data[0]=Min[0]=Min[1]=Max[0]=Max[1]=size=0;}
}T[N];
inline void Fill(node &A,point B){
A=node();A.size=1;
A.data[0]=A.Min[0]=A.Max[0]=B.data[0];
A.data[1]=A.Min[1]=A.Max[1]=B.data[1];
return;
}
inline void update(int u){
if(LS){
T[u].Min[0]=min(T[u].Min[0],T[LS].Min[0]);
T[u].Min[1]=min(T[u].Min[1],T[LS].Min[1]);
T[u].Max[0]=max(T[u].Max[0],T[LS].Max[0]);
T[u].Max[1]=max(T[u].Max[1],T[LS].Max[1]);
}
if(RS){
T[u].Min[0]=min(T[u].Min[0],T[RS].Min[0]);
T[u].Min[1]=min(T[u].Min[1],T[RS].Min[1]);
T[u].Max[0]=max(T[u].Max[0],T[RS].Max[0]);
T[u].Max[1]=max(T[u].Max[1],T[RS].Max[1]);
}
T[u].size=T[LS].size+T[RS].size+1;
return;
}
void Rebuild(int &u,int l,int r,int type)
{
if(l>r) return;int mid=l+r>>1;now=type;nth_element(a+l,a+mid,a+r+1);
u=st[top--];Fill(T[u],a[mid]);
Rebuild(LS,l,mid-1,type^1);Rebuild(RS,mid+1,r,type^1);
update(u);
return;
}
inline void init()
{
Input(n);Input(m);
for(int i=1;i<=n;++i){Input(a[i].data[0]);Input(a[i].data[1]);st[i]=n-i+1;} top=n;
Rebuild(rt,1,n,0);
}
void Push(int u){
if(!u) return;
Push(LS);st[++top]=u,a[top]=(point){T[u].data[0],T[u].data[1]};Push(RS);
return;
}
inline void check(int &u,int type){
top=0;
if(T[u].size*alpha<T[RS].size||T[u].size*alpha<T[LS].size)
Push(u),u=0,Rebuild(u,1,top,type);
return;
}
void insert(int &u,int type,point A) {
if(!u) {u=++n;Fill(T[u],A);return;}
if(A.data[type]<T[u].data[type]) insert(LS,type^1,A);
else insert(RS,type^1,A);
return update(u),check(u,type);
}
inline int Dis(int x1,int y1,int x2,int y2){
if(x1>x2) swap(x1,x2);if(y1>y2) swap(y1,y2);
return x2+y2-x1-y1;
}
inline int get_maxdis(int u,int x,int y){
register int ret=0;
if(x>T[u].Max[0]) ret+=x-T[u].Max[0];
if(x<T[u].Min[0]) ret+=T[u].Min[0]-x;
if(y>T[u].Max[1]) ret+=y-T[u].Max[1];
if(y<T[u].Min[1]) ret+=T[u].Min[1]-y;
return ret;
}
void Query(int u,int x,int y){
if(!u) return;
int dl=INF,dr=INF,d;
d=Dis(T[u].data[0],T[u].data[1],x,y);ans=min(ans,d);
if(LS) dl=get_maxdis(LS,x,y);
if(RS) dr=get_maxdis(RS,x,y);
if(dl<dr) {
if(dl<ans) Query(LS,x,y);
if(dr<ans) Query(RS,x,y);
}
else{
if(dr<ans) Query(RS,x,y);
if(dl<ans) Query(LS,x,y);
}
return;
}
inline void work()
{
int op,x,y;
for(int i=1;i<=m;++i) {
Input(op);Input(x);Input(y);
if(op==1){point A;A.data[0]=x;A.data[1]=y;insert(rt,0,A);}
else{ans=INF;Query(rt,x,y);Print(ans);Putc('\n');}
}
}
}
int main()
{
KD_tree::init();
KD_tree::work();
IO::Flush();
}