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[];
}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 · 巧克力王国」的更多相关文章
- BZOJ1941:[SDOI2010]Hide and Seek(K-D Tree)
		
Description 小猪iPig在PKU刚上完了无聊的猪性代数课,天资聪慧的iPig被这门对他来说无比简单的课弄得非常寂寞,为了消除寂寞感,他决定和他的好朋友giPi(鸡皮)玩一个更加寂寞的游戏- ...
 - BZOJ 1941: [Sdoi2010]Hide and Seek(k-d Tree)
		
Time Limit: 16 Sec Memory Limit: 162 MBSubmit: 1712 Solved: 932[Submit][Status][Discuss] Descripti ...
 - P4475 巧克力王国 k-d tree
		
思路:\(k-d\ tree\) 提交:2次 错因:\(query\)时有一个\(mx\)误写成\(mn\)窝太菜了. 题解: 先把\(k-d\ tree\)建出来,然后查询时判一下整个矩形是否整体\ ...
 - bzoj:1941: [Sdoi2010]Hide and Seek
		
1941: [Sdoi2010]Hide and Seek Time Limit: 16 Sec Memory Limit: 162 MBSubmit: 531 Solved: 295[Submi ...
 - k-d tree模板练习
		
1. [BZOJ]1941: [Sdoi2010]Hide and Seek 题目大意:给出n个二维平面上的点,一个点的权值是它到其他点的最长距离减最短距离,距离为曼哈顿距离,求最小权值.(n< ...
 - 【BZOJ-1941】Hide and Seek      KD-Tree
		
1941: [Sdoi2010]Hide and Seek Time Limit: 16 Sec Memory Limit: 162 MBSubmit: 830 Solved: 455[Submi ...
 - [BZOJ1941][Sdoi2010]Hide and Seek
		
[BZOJ1941][Sdoi2010]Hide and Seek 试题描述 小猪iPig在PKU刚上完了无聊的猪性代数课,天资聪慧的iPig被这门对他来说无比简单的课弄得非常寂寞,为了消除寂寞感,他 ...
 - 【BZOJ1941】[Sdoi2010]Hide and Seek KDtree
		
[BZOJ1941][Sdoi2010]Hide and Seek Description 小猪iPig在PKU刚上完了无聊的猪性代数课,天资聪慧的iPig被这门对他来说无比简单的课弄得非常寂寞,为了 ...
 - 初涉k-d tree
		
听说k-d tree是一个骗分的好东西?(但是复杂度差评??? 还听说绍一的kdt常数特别小? KDT是什么 KDT的全称是k-degree tree,顾名思义,这是一种处理多维空间的数据结构. 例如 ...
 
随机推荐
- CSS清除默认样式代码
			
html, body, div, ul, li, h1, h2, h3, h4, h5, h6, p, dl, dt, dd, ol, form, input, textarea, th, td, s ...
 - BFS(二):数的变换
			
[例1]整数变换(POJ 3278 “Catch That Cow”) 给定两个整数a和b(0 ≤a,b≤100,000),要求把a变换到b.变换规则为:(1)当前数加1:(2)当前数减1:(3)当前 ...
 - Python中的函数及函数参数的使用
			
函数:一个工具,随调随用 降级代码冗余 增加代码的复用性,提高开发效率,为了不成为cv战士 提高程序扩展性 函数有两个阶段:定义阶段,调用阶段. 定义时:只检查函数体内代码语法,不执行函数体内代码. ...
 - Redis持久化深入理解
			
用过Redis的都知道,Redis有两种持久化方式:RDB和AOF,他们的区别大家应该都清楚,所以今天主要想分享一下这两种持久化方式的底层原理以及实现. 如果让你手写一个持久化(架构级)的功能,你没有 ...
 - os.path.join路径拼接的问题
			
问题一: import os a = os.path.join("/test1", "/test2") print(a) b = os.path.join(&q ...
 - 28nm工艺下,自动生成管脚排列文件,给设计加PAD,并在PAD上面打Label的流程(含Tcl脚本)
			
本文转自:自己的微信公众号<数字集成电路设计及EDA教程> 里面主要讲解数字IC前端.后端.DFT.低功耗设计以及验证等相关知识,并且讲解了其中用到的各种EDA工具的教程. 考虑到微信公众 ...
 - Java学习笔记之---单例模型
			
Java学习笔记之---单例模型 单例模型分为:饿汉式,懒汉式 (一)要点 1.某个类只能有一个实例 2.必须自行创建实例 3.必须自行向整个系统提供这个实例 (二)实现 1.只提供私有的构造方法 2 ...
 - 基础篇-1.1走进Java世界
			
在走进Java世界之前,我们势必先了解下Java是什么?Java是一门面向对象的编程语言,是静态面向对象编程语言的代表,极好得实现了面向对象理论,允许程序员以优雅的思维方式进行复杂的编程.Java具有 ...
 - 【字符串】P2084 进制转换-C++
			
题目描述 今天小明学会了进制转换,比如(10101)2 ,那么它的十进制表示的式子就是 : 1*2^4+0*2^3+1*2^2+0*2^1+1*2^0, 那么请你编程实现,将一个M进制的数N转换成十进 ...
 - 洛谷 P1039 侦探推理
			
题目:https://www.luogu.org/problemnew/show/P1039 分析: 这道题是一道有技术含量的模拟,我们主要是不要让计算机向人一样思考,只需要让他穷举变化的星期几和当罪 ...