KD-tree 专题「Hide and Seek · 巧克力王国」

Lockey的瞎理解

  抄了一遍板子又水了俩题,感觉对KD-tree 稍稍理解了一点儿,唠叨一下(二维的KD-tree),如有错误请指出(Lockey 洗脸恭听)

  普通平衡树维护的是一维的序列,但对于二维(可以建平衡树套平衡树,但不好维护)甚至多维就凉了,所以就用到了KD-tree这一神奇的数据结构(伪装“砖家”ing~)

  KD-tree 将二维的平面分别按x,y轮流划分,将平面建成一棵BST ,然后查找,在树中维护当前点所代表的值以及记录以它为根的子树信息(最大值,最小值,值的和,等等),

而通过当前点的信息我们可以判断是否需要往下走,以此来进行查找统计,当然这个判断条件一定要正确(百分百保证不能往下走或需要往下走)

  为了优化时间,KD-tree中进行剪枝也比较重要,如要统计最大值,可以先用估价函数估计该点两个儿子的估值,比较两个儿子,走估值比较大的儿子,如此,等这个儿子回溯回来,最大值有可能被更新,使得原本大于原来的最大值的而可以走的另一个儿子,现在小于当前最大值而不能走,从而实现剪枝

Hide and Seek(中文名:捉迷藏)

KD-tree板子题一道

#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
int n,maximum,minimum,sz,now,ans;
struct Point{
    int x[2];
}sour[510000];
int comp(Point a,Point b){
    return a.x[now]<b.x[now];
}
int dis(Point a,Point b){
    return abs(a.x[0]-b.x[0])+abs(a.x[1]-b.x[1]); 
}
struct KD_Tree{
    KD_Tree *ch[2];
    Point ponit;
    int maxn[2],minn[2];
    void redef(Point a){
        ponit=a,minn[0]=maxn[0]=a.x[0],minn[1]=maxn[1]=a.x[1],ch[0]=ch[1]=NULL;
    }
    void update(KD_Tree *a){
        minn[0]=min(minn[0],a->minn[0]),maxn[0]=max(maxn[0],a->maxn[0]);
        minn[1]=min(minn[1],a->minn[1]),maxn[1]=max(maxn[1],a->maxn[1]);
    }
    void pushup(){
        if(ch[0]) update(ch[0]);
        if(ch[1]) update(ch[1]);
    }
    int calc_min(Point a){
        return max(minn[0]-a.x[0],0)+max(a.x[0]-maxn[0],0)+max(minn[1]-a.x[1],0)+max(a.x[1]-maxn[1],0);
    }
    int calc_max(Point a){
        return max(abs(a.x[0]-minn[0]),abs(a.x[0]-maxn[0]))+max(abs(a.x[1]-minn[1]),abs(a.x[1]-maxn[1]));
    }
}*root,tr[510000];
void build(KD_Tree *&p,int l,int r,int d){
    if(l>r) return;
    p=tr+(sz++),now=d;
    nth_element(sour+l,sour+((l+r)/2),sour+(r+1),comp);
    p->redef(sour[((l+r)/2)]);
    build(p->ch[0],l,((l+r)/2)-1,d^1);
    build(p->ch[1],((l+r)/2)+1,r,d^1);
    p->pushup();  
    
}
void query_max(KD_Tree *p,Point cmp){
    if(p==NULL) return;
    maximum=max(dis(p->ponit,cmp),maximum);
    int Dis[2]={p->ch[0]==NULL?0:p->ch[0]->calc_max(cmp),p->ch[1]==NULL?0:p->ch[1]->calc_max(cmp)};
    int first=Dis[0]>Dis[1]?0:1;
    if(Dis[first]>maximum) query_max(p->ch[first],cmp);
    if(Dis[first^1]>maximum) query_max(p->ch[first^1],cmp);
}
void query_min(KD_Tree *p,Point cmp){
    if(p==NULL) return;
    if(dis(p->ponit,cmp)) minimum=min(dis(p->ponit,cmp),minimum);
    int Dis[2]={p->ch[0]==NULL?0x7f7f7f7f:p->ch[0]->calc_min(cmp),p->ch[1]==NULL?0x7f7f7f7f:p->ch[1]->calc_min(cmp)};
    int first=Dis[0]<Dis[1]?0:1;
    if(Dis[first]<minimum) query_min(p->ch[first],cmp);
    if(Dis[first^1]<minimum) query_min(p->ch[first^1],cmp); 
}
int query_max(Point cmp){
    maximum=0,query_max(root,cmp);
    return maximum;
}
int query_min(Point cmp){
    minimum=0x7fffffff,query_min(root,cmp);
    return minimum;
}
int main(){
    scanf("%d",&n);
    ans=0x7f7f7f7f;
    for(int i=1;i<=n;i++) scanf("%d%d",&sour[i].x[0],&sour[i].x[1]);
    build(root,1,n,0);
    for(int i=1;i<=n;i++){
        ans=min(ans,query_max(sour[i])-query_min(sour[i]));
    }
    printf("%d\n",ans);
}
来颓我啊

 巧克力王国

 每个点记录以它为根的子树中x,y最大值最小值以及子树中的h和,为什么要这样呢?因为如果只记录最小值无法在有负数是判断,只记录最大值和最小值,会超时

    而记录了最大值与最小值不仅可以百分百确定是否往下走,而且能够判断这棵子树中的所有巧克力是不是全都能接受,如果全都能接受的话,就不需要往下走了,直接 $ans+= a*\sumx+b*\sumy $,然后return即可

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
#define ll long long

int n,m,sz,now;
ll sum,a,b,c;

struct Cho{
    ll x[2],h;
}chok[51000];
int comp(Cho a,Cho b){
    return a.x[now]<b.x[now];
}
struct KD_Tree{
    KD_Tree *ch[2];
    Cho qiao;
    ll minn[2],maxn[2],hsum;
    void redef(Cho a){
        qiao=a,minn[0]=maxn[0]=a.x[0],minn[1]=maxn[1]=a.x[1],ch[0]=ch[1]=NULL;
        hsum=a.h;
    }
    void update(KD_Tree *a){
        minn[0]=min(minn[0],a->minn[0]);
        minn[1]=min(minn[1],a->minn[1]);
        maxn[0]=max(maxn[0],a->maxn[0]);
        maxn[1]=max(maxn[1],a->maxn[1]);
        hsum+=a->hsum;
    }
    void pushup(){
        if(ch[0]) update(ch[0]);
        if(ch[1]) update(ch[1]);
    }
    ll calc_C(){
        return min(a*minn[0],a*maxn[0])+min(b*minn[1],b*maxn[1]);
    }
    
}*root,tr[51000];
void build(KD_Tree *&p,int l,int r,int d){
    if(l>r) return;
    p=tr+(sz++),now=d;
    nth_element(chok+l,chok+((l+r)/2),chok+(r+1),comp);
    p->redef(chok[(l+r)/2]);
    build(p->ch[0],l,(l+r)/2-1,d^1);
    build(p->ch[1],(l+r)/2+1,r,d^1);
    p->pushup();
}
int judge(KD_Tree *p){
    ll x=a>0?p->maxn[0]:p->minn[0],
       y=b>0?p->maxn[1]:p->minn[1];
    return a*x+b*y<c;
}
void query_sum(KD_Tree *p){
    if(p==NULL) return;
    if(judge(p)){
        sum+=p->hsum;
        return;
    }
    if((p->qiao.x[0]*a+p->qiao.x[1]*b)<c) sum+=p->qiao.h;
    ll Dis[2]={p->ch[0]==NULL?0:p->ch[0]->calc_C(),p->ch[1]==NULL?0:p->ch[1]->calc_C()};
    if(Dis[0]<c&&p->ch[0]) query_sum(p->ch[0]);
    if(Dis[1]<c&&p->ch[1]) query_sum(p->ch[1]);
}
void query_sum(){
    sum=0;
    query_sum(root);
}
int main(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
        scanf("%lld%lld%lld",&chok[i].x[0],&chok[i].x[1],&chok[i].h);
    build(root,1,n,0);
    while(m--){
        scanf("%lld%lld%lld",&a,&b,&c);
        query_sum();
        printf("%lld\n",sum);
    }
}
View Code

未完待续~

猜你喜欢

转载自www.cnblogs.com/heoitys/p/11297810.html