BZOJ1941Hide and Seek

作KD_tree的入門題。node

問題就是求出任意一個點距其餘點的最大曼哈頓距離和最小曼哈頓距離差,而後對其取min便可。c++

這個東西就是KD_tree能夠輕鬆解決的了。ide

下面總結一下作KD_tree(不帶修)的心得。函數

KD_tree本質上是一顆搜索樹,實際上將全部可能的決策集合不重不漏的劃分了出來,而咱們要作的就是剪枝。ui

其實建KD_tree的過程就是將一個平面劃分紅若干小塊,即子問題,來方便咱們查找,通常的KD_tree須要如下內容:spa

首先左右兒子是必備的。而後注意一個很是重要的事情,咱們劃分的時候是有基準的,咱們創建KD_tree的時候,每個節點都包含着一個真實的點,我稱之爲基準點(BP),咱們遞歸進行的時候就是利用BP來更新ans。接着就是剪枝用的組件,在本題中,咱們維護矩形的4個邊界,只有咱們用估價函數算出的值比較優秀的時候咱們才向下進行,由於估價函數考慮的是最優狀況,若是這種可能性很小的最優狀況都不知足,那麼別的條件就更沒用了,咱們不須要進入這顆子樹。指針

接着一個New函數是KD_tree創建的基礎,咱們將上述組件所有賦好初值,(由於野針確實可pia)。code

pushup函數就如同線段樹通常,咱們維護的信息大多能夠「區間」合併。blog

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

(論循環展開的優越性)

相關文章
相關標籤/搜索