BZOJ1941Hide and Seek

做KD_tree的入门题。

问题就是求出任意一个点距其他点的最大曼哈顿距离和最小曼哈顿距离差,然后对其取min即可。

这个东西就是KD_tree可以轻松解决的了。

下面总结一下做KD_tree(不带修)的心得。

KD_tree本质上是一颗搜索树,实际上将所有可能的决策集合不重不漏的划分了出来,而我们要做的就是剪枝。

其实建KD_tree的过程就是将一个平面划分成若干小块,即子问题,来方便我们查找,一般的KD_tree需要以下内容:

首先左右儿子是必备的。然后注意一个非常重要的事情,我们划分的时候是有基准的,我们建立KD_tree的时候,每一个节点都包含着一个真实的点,我称之为基准点(BP),我们递归进行的时候就是利用BP来更新ans。接着就是剪枝用的组件,在本题中,我们维护矩形的4个边界,只有我们用估价函数算出的值比较优秀的时候我们才向下进行,因为估价函数考虑的是最优情况,如果这种可能性很小的最优情况都不满足,那么别的条件就更没用了,我们不需要进入这颗子树。

接着一个New函数是KD_tree建立的基础,我们将上述组件全部赋好初值,(因为野针确实可pia)。

pushup函数就如同线段树一般,我们维护的信息大多可以“区间”合并。

cal估价函数是KD_tree的灵魂,否则KD_tree有时就是大暴力(其实加了剪枝也像暴力)。

接着开始快乐建树。

r>l就return,开点,换now,nth_element找中位,维护自身信息,递归左右儿子,pushup,建完收工。

然后进行快乐查询。

空指针,return,用当前节点的BP尝试更新答案,求出左右儿子估价值,先跑更优的,因为跑完它以后那个次优的可能就不用跑了。

完成。

#include<bits/stdc++.h>
#define null NULL
using namespace std;
const int N=500010;
const int inf=0x7fffffff;
inline int read(){
    int sum(0),f(1);char x=getchar();
    while(x<'0'||x>'9'){
        if(x=='-') f=-1;
        x=getchar();
    }while(x>='0'&&x<='9'){
        sum=sum*10+x-'0';
        x=getchar();
    }return sum*f;
}
inline int abs_(int x){
    return x<0?-x:x;
}
inline int min_(int x,int y){
    return x<y?x:y;
}
inline int max_(int x,int y){
    return x>y?x:y;
}
int now,n,ans=inf,tot,Maxans,Minans;
struct node{
    int x[2];//x,y坐标
}poi[N];
bool comp(node a,node b){
    return a.x[now]<b.x[now];
}
inline int Mhtdis(node a,node b){
    return abs_(a.x[0]-b.x[0])+abs_(a.x[1]-b.x[1]);
}
struct Kd_tree{
    Kd_tree *ch[2];
    node BP;
    int minv[2],maxv[2];
    inline void New(node a){
        BP=a;
        minv[0]=maxv[0]=a.x[0];
        minv[1]=maxv[1]=a.x[1];
        ch[0]=ch[1]=null;
    }
    inline void pushup(){
        if(ch[0]){
            minv[0]=min_(minv[0],ch[0]->minv[0]);
            maxv[0]=max_(maxv[0],ch[0]->maxv[0]);
            minv[1]=min_(minv[1],ch[0]->minv[1]);
            maxv[1]=max_(maxv[1],ch[0]->maxv[1]);
        }
        if(ch[1]){
            minv[0]=min_(minv[0],ch[1]->minv[0]);
            maxv[0]=max_(maxv[0],ch[1]->maxv[0]);
            minv[1]=min_(minv[1],ch[1]->minv[1]);
            maxv[1]=max_(maxv[1],ch[1]->maxv[1]);
        }
    }
    inline int cal_min(node a){
        return max_(minv[0]-a.x[0],0)+max_(a.x[0]-maxv[0],0)
              +max_(minv[1]-a.x[1],0)+max_(a.x[1]-maxv[1],0);
    }
    inline int cal_max(node a){
        return max_(abs_(a.x[0]-minv[0]),abs_(a.x[0]-maxv[0]))
              +max_(abs_(a.x[1]-minv[1]),abs_(a.x[1]-maxv[1]));
    }
}*root,pool[N];
void build(Kd_tree *&p,int l,int r,int d){
    if(l>r) return ;
    p=pool+(tot++); now=d;
    int mid=l+r>>1;
    nth_element(poi+l,poi+mid,poi+r,comp);
    p->New(poi[mid]);
    build(p->ch[0],l,mid-1,d^1);
    build(p->ch[1],mid+1,r,d^1);
    p->pushup();
}
void query_max(Kd_tree *p,node rec){
    if(p==null) return ;
    Maxans=max_(Mhtdis(p->BP,rec),Maxans);
    int dis[2]={p->ch[0]==null?0:p->ch[0]->cal_max(rec),
                p->ch[1]==null?0:p->ch[1]->cal_max(rec)};
    int first=dis[0]>dis[1]?0:1;
    if(dis[first]>Maxans) query_max(p->ch[first],rec);
    if(dis[first^1]>Maxans) query_max(p->ch[first^1],rec);
}
void query_min(Kd_tree *p,node rec){
    if(p==null) return ;
    if(Mhtdis(p->BP,rec))
        Minans=min_(Mhtdis(p->BP,rec),Minans);
    int dis[2]={p->ch[0]==null?inf:p->ch[0]->cal_min(rec),
                p->ch[1]==null?inf:p->ch[1]->cal_min(rec)};
    int first=dis[0]<dis[1]?0:1;
    if(dis[first]<Minans) query_min(p->ch[first],rec);
    if(dis[first^1]<Minans) query_min(p->ch[first^1],rec);
}
inline int query_max(node rec){
    Maxans=0;
    query_max(root,rec);
    return Maxans;
}
inline int query_min(node rec){
    Minans=inf;
    query_min(root,rec);
    return Minans;
}
int main(){
    n=read();
    for(int i=1;i<=n;++i){
        poi[i].x[0]=read();
        poi[i].x[1]=read();
    }
    build(root,1,n,0);
    for(int i=1;i<=n;++i)
        ans=min_(ans,query_max(poi[i])-query_min(poi[i]));
    printf("%d",ans);
    return 0;
}
View Code

(论循环展开的优越性)

猜你喜欢

转载自www.cnblogs.com/Yu-shi/p/11299215.html