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[];
}sour[];
int comp(Point a,Point b){
return a.x[now]<b.x[now];
}
int dis(Point a,Point b){
return abs(a.x[]-b.x[])+abs(a.x[]-b.x[]);
}
struct KD_Tree{
KD_Tree *ch[];
Point ponit;
int maxn[],minn[];
void redef(Point a){
ponit=a,minn[]=maxn[]=a.x[],minn[]=maxn[]=a.x[],ch[]=ch[]=NULL;
}
void update(KD_Tree *a){
minn[]=min(minn[],a->minn[]),maxn[]=max(maxn[],a->maxn[]);
minn[]=min(minn[],a->minn[]),maxn[]=max(maxn[],a->maxn[]);
}
void pushup(){
if(ch[]) update(ch[]);
if(ch[]) update(ch[]);
}
int calc_min(Point a){
return max(minn[]-a.x[],)+max(a.x[]-maxn[],)+max(minn[]-a.x[],)+max(a.x[]-maxn[],);
}
int calc_max(Point a){
return max(abs(a.x[]-minn[]),abs(a.x[]-maxn[]))+max(abs(a.x[]-minn[]),abs(a.x[]-maxn[]));
}
}*root,tr[];
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)/),sour+(r+),comp);
p->redef(sour[((l+r)/)]);
build(p->ch[],l,((l+r)/)-,d^);
build(p->ch[],((l+r)/)+,r,d^);
p->pushup(); }
void query_max(KD_Tree *p,Point cmp){
if(p==NULL) return;
maximum=max(dis(p->ponit,cmp),maximum);
int Dis[]={p->ch[]==NULL?:p->ch[]->calc_max(cmp),p->ch[]==NULL?:p->ch[]->calc_max(cmp)};
int first=Dis[]>Dis[]?:;
if(Dis[first]>maximum) query_max(p->ch[first],cmp);
if(Dis[first^]>maximum) query_max(p->ch[first^],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[]={p->ch[]==NULL?0x7f7f7f7f:p->ch[]->calc_min(cmp),p->ch[]==NULL?0x7f7f7f7f:p->ch[]->calc_min(cmp)};
int first=Dis[]<Dis[]?:;
if(Dis[first]<minimum) query_min(p->ch[first],cmp);
if(Dis[first^]<minimum) query_min(p->ch[first^],cmp);
}
int query_max(Point cmp){
maximum=,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=;i<=n;i++) scanf("%d%d",&sour[i].x[],&sour[i].x[]);
build(root,,n,);
for(int i=;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[],h;
}chok[];
int comp(Cho a,Cho b){
return a.x[now]<b.x[now];
}
struct KD_Tree{
KD_Tree *ch[];
Cho qiao;
ll minn[],maxn[],hsum;
void redef(Cho a){
qiao=a,minn[]=maxn[]=a.x[],minn[]=maxn[]=a.x[],ch[]=ch[]=NULL;
hsum=a.h;
}
void update(KD_Tree *a){
minn[]=min(minn[],a->minn[]);
minn[]=min(minn[],a->minn[]);
maxn[]=max(maxn[],a->maxn[]);
maxn[]=max(maxn[],a->maxn[]);
hsum+=a->hsum;
}
void pushup(){
if(ch[]) update(ch[]);
if(ch[]) update(ch[]);
}
ll calc_C(){
return min(a*minn[],a*maxn[])+min(b*minn[],b*maxn[]);
} }*root,tr[];
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)/),chok+(r+),comp);
p->redef(chok[(l+r)/]);
build(p->ch[],l,(l+r)/-,d^);
build(p->ch[],(l+r)/+,r,d^);
p->pushup();
}
int judge(KD_Tree *p){
ll x=a>?p->maxn[]:p->minn[],
y=b>?p->maxn[]:p->minn[];
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[]*a+p->qiao.x[]*b)<c) sum+=p->qiao.h;
ll Dis[]={p->ch[]==NULL?:p->ch[]->calc_C(),p->ch[]==NULL?:p->ch[]->calc_C()};
if(Dis[]<c&&p->ch[]) query_sum(p->ch[]);
if(Dis[]<c&&p->ch[]) query_sum(p->ch[]);
}
void query_sum(){
sum=;
query_sum(root);
}
int main(){
scanf("%d%d",&n,&m);
for(int i=;i<=n;i++)
scanf("%lld%lld%lld",&chok[i].x[],&chok[i].x[],&chok[i].h);
build(root,,n,);
while(m--){
scanf("%lld%lld%lld",&a,&b,&c);
query_sum();
printf("%lld\n",sum);
}
}

JZPFAR

  题意是说要找距离第k大,一开始我是想维护一个大根堆,从堆顶pop,一直删k-1次后,堆顶的位置就是ans,然后发现为了剪枝我们需要找到当前堆中第K大的距离,与要插入的值(或估值)比较,如果比当前第K大还小就不插入,理论上好像能行,但是操作起来有些麻烦

  因此改用了小根堆

  对于每组询问,维护一个大小为Ki的小根堆,当大小小于Ki时见谁插谁;当大小等于Ki,只有在当前的距离(或估值)大于堆顶时才可加入,且每加入一个就pop一个保持堆的大小。

  最后,大小为K的堆表示,所有距离中最大的K个距离,而堆顶就是第K大

  PS: 应该可以用手写堆优化,但没打对,好像是死循环,改了一下午,有时间要多多的练习一下手写堆

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<queue>
using namespace std;
#define INF 0x7fffffff
#define int long long
int n,m,sz,now,px,py,pk; int top;//大根堆
struct Queue{
int val,pos;
Queue(int a=,int b=){val=a,pos=b;}
bool operator < (const Queue &a) const{
return val==a.val?pos<a.pos:val>a.val;
}
};
priority_queue<Queue>q;
struct node{
int x[],id;
}sour[]; int comp(node a,node b){return a.x[now]<b.x[now];}
int multi(int a){return a*a;} struct KD_Tree{
KD_Tree *ch[];
node po;
int maxn[],minn[];
void redef(node a){
po=a,maxn[]=minn[]=a.x[],maxn[]=minn[]=a.x[],ch[]=ch[]=NULL;
}
void update(KD_Tree *a){
maxn[]=max(maxn[],a->maxn[]),minn[]=min(minn[],a->minn[]);
maxn[]=max(maxn[],a->maxn[]),minn[]=min(minn[],a->minn[]);
}
void pushup(){
if(ch[]) update(ch[]);
if(ch[]) update(ch[]);
}
int clac_max(){
return max(multi(maxn[]-px),multi(px-minn[]))+max(multi(maxn[]-py),multi(py-minn[]));
}
}*root,tr[]; int len(KD_Tree *p){
return multi(p->po.x[]-px)+multi(p->po.x[]-py);
} 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)/),sour+r+,comp);
p->redef(sour[(l+r)/]);
build(p->ch[],l,(l+r)/-,d^);
build(p->ch[],(l+r)/+,r,d^);
p->pushup();
} void query_kth(KD_Tree *p){
if(p==NULL) return;
if(top<pk||len(p)>q.top().val||(len(p)==q.top().val&&p->po.id<q.top().pos)){
q.push(Queue(len(p),p->po.id));
if(q.size()>pk) q.pop();
}
int Dis[]={
p->ch[]==NULL?-INF:p->ch[]->clac_max(),
p->ch[]==NULL?-INF:p->ch[]->clac_max()
};
int first=Dis[]>Dis[]?:;
if(q.size()<pk||Dis[first]>=q.top().val){
query_kth(p->ch[first]);
}
if(q.size()<pk||Dis[first^]>=q.top().val){
query_kth(p->ch[first^]);
}
}
void query_kth(){
while(q.size()) q.pop();
query_kth(root);
}
signed main(){
scanf("%lld",&n);
for(int i=;i<=n;i++)
scanf("%lld%lld",&sour[i].x[],&sour[i].x[]),sour[i].id=i;
build(root,,n,);
scanf("%lld",&m);
while(m--){
scanf("%lld%lld%lld",&px,&py,&pk);
query_kth();
printf("%lld\n",q.top().pos);
}
}

K远点对

  求距离第K远的点对的距离,和上一题一样,只不过小根堆维护的是全局第K大,不需要再每次清空了,

  for循环枚举所有的点作为一端,再走一遍就行,注意(1,2)和(2,1)是同一点对,只能算一次

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<queue>
using namespace std;
#define INF 0x7fffffff
#define ll long long
ll n,m,sz,now,px,py,yxm; struct Queue{
ll val;
Queue(ll a=){val=a;}
bool operator < (const Queue &a) const{
return val>a.val;
}
};
priority_queue<Queue>q;
struct node{
ll x[],id;
}sour[]; ll comp(node a,node b){return a.x[now]<b.x[now];}
ll multi(ll a){return a*a;} struct KD_Tree{
KD_Tree *ch[];
node po;
ll maxn[],minn[];
void redef(node a){
po=a,maxn[]=minn[]=a.x[],maxn[]=minn[]=a.x[],ch[]=ch[]=NULL;
}
void update(KD_Tree *a){
maxn[]=max(maxn[],a->maxn[]),minn[]=min(minn[],a->minn[]);
maxn[]=max(maxn[],a->maxn[]),minn[]=min(minn[],a->minn[]);
}
void pushup(){
if(ch[]) update(ch[]);
if(ch[]) update(ch[]);
}
ll clac_max(){
return max(multi(maxn[]-px),multi(px-minn[]))+max(multi(maxn[]-py),multi(py-minn[]));
}
}*root,tr[]; ll len(KD_Tree *p){
return multi(p->po.x[]-px)+multi(p->po.x[]-py);
} void build(KD_Tree *&p,ll l,ll r,ll d){
if(l>r) return;
p=tr+(sz++),now=d;
nth_element(sour+l,sour+((l+r)/),sour+r+,comp);
p->redef(sour[(l+r)/]);
build(p->ch[],l,(l+r)/-,d^);
build(p->ch[],(l+r)/+,r,d^);
p->pushup();
}
ll judge(KD_Tree *p){
return q.size()<m||len(p)>=q.top().val;
}
void query_kth(KD_Tree *p){
if(p==NULL) return;
if(p->po.id>yxm&&judge(p)){
q.push(Queue(len(p)));
if(q.size()>m) q.pop();
}
ll Dis[]={p->ch[]==NULL?-INF:p->ch[]->clac_max(),p->ch[]==NULL?-INF:p->ch[]->clac_max()};
ll first=Dis[]>Dis[]?:;
if(q.size()<m||Dis[first]>=q.top().val) query_kth(p->ch[first]);
if(q.size()<m||Dis[first^]>=q.top().val) query_kth(p->ch[first^]);
}
void query_kth(){
query_kth(root);
}
signed main(){
scanf("%lld%lld",&n,&m);
for(int i=;i<=n;i++)
scanf("%lld%lld",&sour[i].x[],&sour[i].x[]),sour[i].id=i;
build(root,,n,);
for(int i=;i<=n;i++){
px=sour[i].x[],py=sour[i].x[],yxm=sour[i].id;
query_kth();
}
// while(q.size())
printf("%lld\n",q.top().val);
}

未完待续~

KD-tree 专题「Hide and Seek · 巧克力王国」的更多相关文章

  1. BZOJ1941:[SDOI2010]Hide and Seek(K-D Tree)

    Description 小猪iPig在PKU刚上完了无聊的猪性代数课,天资聪慧的iPig被这门对他来说无比简单的课弄得非常寂寞,为了消除寂寞感,他决定和他的好朋友giPi(鸡皮)玩一个更加寂寞的游戏- ...

  2. BZOJ 1941: [Sdoi2010]Hide and Seek(k-d Tree)

    Time Limit: 16 Sec  Memory Limit: 162 MBSubmit: 1712  Solved: 932[Submit][Status][Discuss] Descripti ...

  3. P4475 巧克力王国 k-d tree

    思路:\(k-d\ tree\) 提交:2次 错因:\(query\)时有一个\(mx\)误写成\(mn\)窝太菜了. 题解: 先把\(k-d\ tree\)建出来,然后查询时判一下整个矩形是否整体\ ...

  4. bzoj:1941: [Sdoi2010]Hide and Seek

    1941: [Sdoi2010]Hide and Seek Time Limit: 16 Sec  Memory Limit: 162 MBSubmit: 531  Solved: 295[Submi ...

  5. k-d tree模板练习

    1. [BZOJ]1941: [Sdoi2010]Hide and Seek 题目大意:给出n个二维平面上的点,一个点的权值是它到其他点的最长距离减最短距离,距离为曼哈顿距离,求最小权值.(n< ...

  6. 【BZOJ-1941】Hide and Seek KD-Tree

    1941: [Sdoi2010]Hide and Seek Time Limit: 16 Sec  Memory Limit: 162 MBSubmit: 830  Solved: 455[Submi ...

  7. [BZOJ1941][Sdoi2010]Hide and Seek

    [BZOJ1941][Sdoi2010]Hide and Seek 试题描述 小猪iPig在PKU刚上完了无聊的猪性代数课,天资聪慧的iPig被这门对他来说无比简单的课弄得非常寂寞,为了消除寂寞感,他 ...

  8. 【BZOJ1941】[Sdoi2010]Hide and Seek KDtree

    [BZOJ1941][Sdoi2010]Hide and Seek Description 小猪iPig在PKU刚上完了无聊的猪性代数课,天资聪慧的iPig被这门对他来说无比简单的课弄得非常寂寞,为了 ...

  9. 初涉k-d tree

    听说k-d tree是一个骗分的好东西?(但是复杂度差评??? 还听说绍一的kdt常数特别小? KDT是什么 KDT的全称是k-degree tree,顾名思义,这是一种处理多维空间的数据结构. 例如 ...

随机推荐

  1. Springboot 连接 使用 Redis Example

    通过一个简单的例子使用Springboot 连接并使用Redis. 本文假设已经安装好Redis. 1.首先将URL转换为一个ID ,并使用 StringRedisTemplate 将ID 和 URL ...

  2. Programming In Lua 第八章

    1, 也就是说,lua虽然会把代码预编译成中间码,以提高运行速度.但其会在程序运行过程中需要编译器,所以其仍然是解释型语言.loadfile会加载一个文件并将其编译成中间码,并返回一个函数. 2, 3 ...

  3. 浅谈Linq查询

    一.Var关键字 在学习Linq查询之前,我们先来学习var关键字的用法,看看微软官方的定义:从Visual C#3.0开始,在方法范围声明的变量可以具有隐式“类型” var.隐式类型的局部变量是强类 ...

  4. ElasticSearch 7.1.1 集群环境搭建

    1. 集群简介 三台机器,均用于保存数据且可被选为master节点 服务版本 服务 版本 elasticsearch 7.1.1 jdk 1.8 1. 创建elsearch用户 不建议直接使用root ...

  5. SpringBoot使用Docker快速部署项目

    1.简介 建议阅读本文最好对Dokcer有一些了解 首先我们先了解一下Docker是什么 Docker 属于 Linux 容器的一种封装,提供简单易用的容器使用接口.它是目前最流行的 Linux 容器 ...

  6. linux上mysql MM(双主)架构及keepalived搭建

    master1 10.1.1.14 VIP 10.1.1.16master2 10.1.1.15 VIP 10.1.1.16 一.mysql MM配置1.修改master1的my.cnf# vi /e ...

  7. apache开启vhost后localhost和127.0.0.1无法访问

    自己单独搭建了php+mysql+apach+windows环境:后面又开启apache的虚拟主机vhost;然后自己配置虚拟主机站点可以正常访问,但是localhost和127.0.0.1无法访问, ...

  8. Bzoj 1229: [USACO2008 Nov]toy 玩具 题解 三分+贪心

    1229: [USACO2008 Nov]toy 玩具 Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 338  Solved: 136[Submit] ...

  9. 快速掌握mongoDB(二)——聚合管道和MapReduce

    上一节简单介绍了一下mongoDB的增删改查操作,这一节将介绍其聚合操作.我们在使用mysql.sqlserver时经常会用到一些聚合函数,如sum/avg/max/min/count等,mongoD ...

  10. redis分布式锁的问题和解决

    分布式锁 在分布式环境中,为了保证业务数据的正常访问,防止出现重复请求的问题,会使用分布式锁来阻拦后续请求.具体伪代码如下: public void doSomething(String userId ...