KD Tree 新知选作(BZOJ 2850)

什么是KDrree


这里写图片描述


一些优质入门文章

在这里附上两篇笔者学习kdtree的思路和详细实现思想的优质文章:

思路篇传送门
详细篇传送门

同时鸣谢为笔者耐心讲解kdtree的神犇:mima_reincarnation


笔者对KDtree的理解
应用

在去年的ACM/ICPC的青岛赛区,有一道金牌题就是KDtree题,但在此之前某人曾立下flag“应该不会考的吧,但是考了就GG”。

主要用于解决kNN问题,平面上距离最近点对、平面上距离最远点对等等问题。在工程中作用颇大,在OI中用武之地有限。

·

思路

对于KDtree的基本思路:

  • 把一个多维体拆碎成多个小多维体,整体思路类似线段树的实现。类比地讲,线段树要分割的是一个一维的线段,而KDtree要分割的是多维体(如二维平面,三维空间……),所以KDtree要对于不同的维度进行“拆分”,以方便进行统计操作。

对于KDtree的建树思路:

  • 以二维平面为例,通俗地讲,KDtree建树的大体思路就是“先横着分,再竖着分,再横着分,再竖着分……一直分到只有一个点”。对比线段树来讲,线段树的每个节点表示的是一个区间,而一棵点树表示的才是某个数字;而在KDtree中,每一个节点都能表示一个点,(以二维KDtree为例)且能表示以这个节点为分割点,以某个维度为分割方式的一个分割。

对于KDtree的查询思路:

  • 由建树的思路可知,每个结点既然能表示一个分割,那么每个结点都能都表示出点集氛围内的一片区域,如在二维KDtree每个结点可表示出一个矩形。那么根据“该点是否在矩形内”、“该矩形是否符合条件”等依据题意应用的条件便可实现查询。

引图例

这里写图片描述
这里写图片描述

图例引自【量化课堂】,链接在文首的“详细篇传送门”


·
·

KDtree例题讲


BZOJ 2850 巧克力王国

原题描述

Description
巧克力王国里的巧克力都是由牛奶和可可做成的。但是并不是每一块巧克力都受王国人民的欢迎,因为大家都不喜欢过于甜的巧克力。对于每一块巧克力,我们设x和y为其牛奶和可可的含量。由于每个人对于甜的程度都有自己的评判标准,所以每个人都有两个参数a和b,分别为他自己为牛奶和可可定义的权重,因此牛奶和可可含量分别为x和y的巧克力对于他的甜味程度即为ax + by。而每个人又有一个甜味限度c,所有甜味程度大于等于c的巧克力他都无法接受。每块巧克力都有一个美味值h。现在我们想知道对于每个人,他所能接受的巧克力的美味值之和为多少

Input
第一行两个正整数n和m,分别表示巧克力个数和询问个数。接下来n行,每行三个整数x,y,h,含义如题目所示。再
接下来m行,每行三个整数a,b,c,含义如题目所示。

Output
输出m行,其中第i行表示第i个人所能接受的巧克力的美味值之和。


思路&题解

题目给出了一些平面上有权值的点,并给出了一个约束条件,问符合该约束条件的点权值和为多少。

给出的约束条件可以表示为a*x+b*y < c,那么该约束条件可以转化为一个半平面交。考虑KDtree的做法,当某个矩形的四个点都在半平面交内时,所以矩形内的所有点都在半平面交内,某个矩形的四个点都不在半平面交内的时候,所以矩形内的所有点都不在半平面内,如果某个矩形部分在本平面交内的时候,就递归求解。

思路
/**************************************************************
    Problem: 2850
    User: CHN
    Language: C++
    Result: Accepted
    Time:33100 ms
    Memory:5344 kb
****************************************************************/

#include<cstdio>
#include<iostream>
#include<cmath>
#include<algorithm>
#include<cstring>
#include<vector>

using namespace std;

const int maxn=int(5e4)+10;
const int INF=2147483647;

int n,m;
int root;

struct Point {
    int d[2];
    int h;
    Point(int x,int y,int _) {
        d[0]=x,d[1]=y,h=_;
    }
    Point() {}
    void read() {
        scanf("%d%d%d",&d[0],&d[1],&h);
        return;
    }
}p[maxn];

bool PATH=0;
bool cmp(Point a,Point b) {
    return a.d[PATH]<b.d[PATH];
}

bool can_calc(int x,int y,long long a,long long b,long long c) {
    return a*x+b*y<c;
}

struct Node {
    Point *pt;
    int x,y,u,v;
    int ls,rs;
    long long sum;
    Node() {ls=rs=sum=0; pt=0x0;}
    void print() {
        printf("ptx:%d pty:%d sum:%lld x:%d y:%d u:%d v:%d ls:%d rs:%d\n",pt->d[0],pt->d[1],sum,x,y,u,v,ls,rs);
        return;
    }
    int check(long long a,long long b,long long c) {
        int tot=0;
        tot+=can_calc(x,y,a,b,c);
        tot+=can_calc(u,y,a,b,c);
        tot+=can_calc(x,v,a,b,c);
        tot+=can_calc(u,v,a,b,c);
        return tot;
    }
};
vector<Node> node;

void update(int k) {
    int ls=node[k].ls,rs=node[k].rs;
    node[k].sum=1LL*node[k].pt->h;
    if(ls) {
        node[k].x=min(node[k].x,node[ls].x),node[k].y=min(node[k].y,node[ls].y);
        node[k].u=max(node[k].u,node[ls].u),node[k].v=max(node[k].v,node[ls].v);
        node[k].sum+=node[ls].sum;
    }
    if(rs) {
        node[k].x=min(node[k].x,node[rs].x),node[k].y=min(node[k].y,node[rs].y);
        node[k].u=max(node[k].u,node[rs].u),node[k].v=max(node[k].v,node[rs].v);
        node[k].sum+=node[rs].sum;
    }
    return;
}

int build(int l,int r,int dim) {
    node.push_back(Node());
    int k=node.size()-1;
    int mid=(l+r)>>1;
    PATH=dim;
    nth_element(p+l,p+mid,p+r+1,cmp);
    node[k].pt=p+mid;
    node[k].x=node[k].u=node[k].pt->d[0];
    node[k].y=node[k].v=node[k].pt->d[1];
    if(l==r) {
        node[k].sum=1LL*node[k].pt->h;
        return k;
    }

    int ll=0,rr=0;
    if(mid>l) ll=build(l,mid-1,dim^1);
    rr=build(mid+1,r,dim^1);
    node[k].ls=ll;
    node[k].rs=rr;
    update(k);
    return k;
}

long long query(int k,int a,int b,int c) {
    if(!k) return 0;
    int tot=node[k].check(a,b,c);
    if(tot==4) return node[k].sum;
    if(tot==0) return 0;
    long long ans=0;
    if(can_calc(node[k].pt->d[0],node[k].pt->d[1],a,b,c)) ans+=1LL*node[k].pt->h;
    return ans+query(node[k].ls,a,b,c)+query(node[k].rs,a,b,c);
}

int main() {
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++) p[i].read();

    node.push_back(Node());
    root=build(1,n,0);

    for(int i=1;i<=m;i++) {
        int a,b,c;
        scanf("%d%d%d",&a,&b,&c);
        printf("%lld\n",query(root,a,b,c));
    }
    return 0;
}
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值