LG4169 [Violet]天使玩偶/SJY摆棋子

题意

Ayu 在七年前曾经收到过一个天使玩偶,当时她把它当作时间囊埋在了地下。而七年后 的今天,Ayu 却忘了她把天使玩偶埋在了哪里,所以她决定仅凭一点模糊的记忆来寻找它。

我们把 Ayu 生活的小镇看作一个二维平面坐标系,而 Ayu 会不定时地记起可能在某个点 (xmy) 埋下了天使玩偶;或者 Ayu 会询问你,假如她在 (x,y) ,那么她离近的天使玩偶可能埋下的地方有多远。

因为 Ayu 只会沿着平行坐标轴的方向来行动,所以在这个问题里我们定义两个点之间的距离为dist(A,B)=|Ax-Bx|+|Ay-By|。其中 Ax 表示点 A的横坐标,其余类似。

n,m<=300 000

xi,yi<=1 000 000

分析

kd-tree模板题。

  1. 首先依次按照每一维(即先按照x,再按照y,再按照x…多维同理)将点存在一棵二叉树中:
    先求出以当前维数为关键字的中间点是谁(用到nth_element这个函数,可以直接把排名为k的放在第k位上,不保证其他有序:nth_element(a+1,a+k,a+1+n,cmp))
    为了一会儿查询中求估价函数方便,需要记录一下当前节点的子树中各维的极值(max,min)

  2. 插入操作插入就按照每个维度往左右子树插就行......可是令人绝望的是,这样子插了若干次之后,可能原树就被退化成一个近似于链的东西了.这个时候,我们就要利用替罪羊树的思想,设一个α=0.75,如果对于点x,x的左右子树中的任意一棵的大小大于了x子树大小乘以α,说明原树已经非常不平衡了,需要将其还原成一个点的序列(这一过程被称为拍扁),然后重建一次.当然,你也可以根据你的心情对α值做调整.

  3. 询问操作的本质是搜索+用估价函数剪枝(估价函数的结果一定比实际最优解小),先搜索更优的,用更优的那个子树更新答案;判断次优的子树的估价函数是否小于当前答案,可以发现这个概率是比较小的,进入次优子树搜索的概率就小一些。

时间复杂度?我不知道,反正现在没人会来卡。

代码

#include<bits/stdc++.h>
#define rg register
#define il inline
#define co const
template<class T>il T read(){
    rg T data=0,w=1;
    rg char ch=getchar();
    while(!isdigit(ch)){
        if(ch=='-') w=-1;
        ch=getchar();
    }
    while(isdigit(ch))
        data=data*10+ch-'0',ch=getchar();
    return data*w;
}
template<class T>il T read(rg T&x){
    return x=read<T>();
}
typedef long long ll;
using namespace std;

#define ratio 0.75
co int N=1e6+1,INF=0x3f3f3f3f;
struct point{int x[2];}p[N];
struct node{int mi[2],mx[2],ls,rs,sz;point tp;}tr[N];
int n,m,rt,cur,top,WD,ans,rub[N];
int operator<(co point&a,co point&b){
    return a.x[WD]<b.x[WD];
}
int newnode(){
    return top?rub[top--]:++cur;
}
void up(int k){
    int l=tr[k].ls,r=tr[k].rs;
    for(int i=0;i<2;++i){
        tr[k].mi[i]=tr[k].mx[i]=tr[k].tp.x[i];
        if(l) tr[k].mi[i]=min(tr[k].mi[i],tr[l].mi[i]),tr[k].mx[i]=max(tr[k].mx[i],tr[l].mx[i]);
        if(r) tr[k].mi[i]=min(tr[k].mi[i],tr[r].mi[i]),tr[k].mx[i]=max(tr[k].mx[i],tr[r].mx[i]);
    }
    tr[k].sz=tr[l].sz+tr[r].sz+1;
}
int build(int l,int r,int wd){
    if(l>r) return 0;
    int k=newnode(),mid=(l+r)>>1;
    WD=wd,nth_element(p+l,p+mid,p+r+1),tr[k].tp=p[mid];
    tr[k].ls=build(l,mid-1,wd^1),tr[k].rs=build(mid+1,r,wd^1);
    up(k);return k;
}
void pia(int k,int num){
    if(tr[k].ls) pia(tr[k].ls,num);
    p[num+tr[tr[k].ls].sz+1]=tr[k].tp,rub[++top]=k;
    if(tr[k].rs) pia(tr[k].rs,num+tr[tr[k].ls].sz+1);
}
void check(int&k,int wd){
    if(ratio*tr[k].sz<tr[tr[k].ls].sz||ratio*tr[k].sz<tr[tr[k].rs].sz)
        pia(k,0),k=build(1,tr[k].sz,wd);
}
void ins(co point&tmp,int&k,int wd){
    if(!k) {k=newnode(),tr[k].tp=tmp,tr[k].ls=tr[k].rs=0,up(k);return;}
    if(tr[k].tp.x[wd]<tmp.x[wd]) ins(tmp,tr[k].rs,wd^1);
    else ins(tmp,tr[k].ls,wd^1);
    up(k),check(k,wd);
}
int getdis(co point&tmp,int k){
    int re=0;
    for(int i=0;i<2;++i)
        re+=max(0,tmp.x[i]-tr[k].mx[i])+max(0,tr[k].mi[i]-tmp.x[i]);
    return re;
}
int dist(co point&a,co point&b) {return abs(a.x[0]-b.x[0])+abs(a.x[1]-b.x[1]);}
void query(co point&tmp,int k){
    ans=min(ans,dist(tmp,tr[k].tp));
    int dl=INF,dr=INF;
    if(tr[k].ls) dl=getdis(tmp,tr[k].ls);
    if(tr[k].rs) dr=getdis(tmp,tr[k].rs);
    if(dl<dr){
        if(dl<ans) query(tmp,tr[k].ls);
        if(dr<ans) query(tmp,tr[k].rs);
    }
    else{
        if(dr<ans) query(tmp,tr[k].rs);
        if(dl<ans) query(tmp,tr[k].ls);
    }
}
int main(){
//  freopen(".in","r",stdin);
//  freopen(".out","w",stdout);
    int bj;
    read(n),read(m);
    for(int i=1;i<=n;++i) read(p[i].x[0]),read(p[i].x[1]);
    rt=build(1,n,0);
    while(m--){
        point tmp;
        read(bj),read(tmp.x[0]),read(tmp.x[1]);
        if(bj==1) ins(tmp,rt,0);
        else ans=INF,query(tmp,rt),printf("%d\n",ans);
    }
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/autoint/p/10405853.html