KD-Tree复习笔记(BZOJ1941 & BZOJ2648 & BZOJ4066)
快一年了都没碰到什么必须用KDT的题目导致模板完全忘光了,重新复习了一下。
K_Dimention_Tree是一种用来处理二维以上问题的数据结构(OI中一般都是二维),本质是二维启发式估价函数实现剪枝(实际上就是暴搜的优化),随机数据是大常数$O(n\log n)$,构造数据是$O(n\sqrt{n})$,但是好像可以过50W。
先说一下实现:
Build() : 对于二维中的一系列点,枚举x坐标中位数的一条直线,直线两边的点分别划为左右子树,下一层枚举y坐标中位数,不断交替进行。
Ins() : 同样x,y坐标交替作为关键字插入,类似平衡数,代码很简短。
Que() : 先求出两棵子树的估价函数,选估价更优的递归下去,然后递归另一个(如果估价比当前答案还要劣则剪枝)。由此可知,估价函数和A-Star中的启发式函数一样,必须保证估价比实际优。
下面分别说一下两个模型的启发式函数:
1.曼哈顿最小值和最大值:
int getmn(P a){ int s=; rep(i,,) s+=max(T[i]-a.mx[i],),s+=max(a.mn[i]-T[i],); return s; }
int getmx(P a){ int s=; rep(i,,) s+=max(abs(T[i]-a.mx[i]),abs(T[i]-a.mn[i])); return s; }
2.欧氏距离最大值:
int getmx(P a){ int s=; rep(i,,) s+=sqr(max(abs(T[i]-a.mx[i]),abs(T[i]-a.mn[i]))); return s; }
几个注意点:
1.并不一定需要启发式函数,只需要普通的显然的剪枝,KD树也不一定是求最优化,见下面例题。
2.模板稍长且很容易写错,并且很多时候出错不体现在WA而是TLE,写的时候一定要注意细节。
3.build因为有时是rebuild,所以没有数的地方要返回0(被坑了好多次)
下面是例题:
1. BZOJ1941
#include<cstdio>
#include<cstring>
#include<algorithm>
#define rep(i,l,r) for (int i=l; i<=r; i++)
typedef long long ll;
using namespace std; const int N=,inf=;
int n,ans=inf,res,rt,F=; inline void cmin(int &x,int y){ if (y<x) x=y; }
inline void cmax(int &x,int y){ if (y>x) x=y; }
int abs(int x){ return (x<) ? -x : x; } struct P{
int d[],mx[],mn[],l,r;
int& operator [](int x){ return d[x]; }
friend bool operator <(P a,P b){ return a[F]<b[F]; }
friend int dis(P a,P b){ return abs(a[]-b[])+abs(a[]-b[]); }
}p[N],t[N],T; void upd(int x){
int l=t[x].l,r=t[x].r;
rep(i,,){
t[x].mn[i]=t[x].mx[i]=t[x][i];
if (l) cmin(t[x].mn[i],t[l].mn[i]),cmax(t[x].mx[i],t[l].mx[i]);
if (r) cmin(t[x].mn[i],t[r].mn[i]),cmax(t[x].mx[i],t[r].mx[i]);
}
} int build(int l,int r,int k){
if (l>r) return ;
F=k; int mid=(l+r)>>;
nth_element(p+l,p+mid,p+r+); t[mid]=p[mid];
rep(i,,) t[mid].mn[i]=t[mid].mx[i]=t[mid][i];
t[mid].l=build(l,mid-,k^); t[mid].r=build(mid+,r,k^);
upd(mid); return mid;
} int getmn(P a){ int s=; rep(i,,) s+=max(T[i]-a.mx[i],),s+=max(a.mn[i]-T[i],); return s; }
int getmx(P a){ int s=; rep(i,,) s+=max(abs(T[i]-a.mx[i]),abs(T[i]-a.mn[i])); return s; } void quemn(int x){
int tmp=dis(T,t[x]);
if (tmp) res=min(res,tmp);
int l=t[x].l,r=t[x].r,dl=inf,dr=inf;
if (l) dl=getmn(t[l]); if (r) dr=getmn(t[r]);
if (dl<dr) { if (dl<res) quemn(l); if (dr<res) quemn(r); }
else { if (dr<res) quemn(r); if (dl<res) quemn(l); }
} void quemx(int x){
res=max(res,dis(T,t[x]));
int l=t[x].l,r=t[x].r,dl=-inf,dr=-inf;
if (l) dl=getmx(t[l]); if (r) dr=getmx(t[r]);
if (dl>dr) { if (dl>res) quemx(l); if (dr>res) quemx(r); }
else { if (dr>res) quemx(r); if (dl>res) quemx(l); }
} int que(int f,int x,int y){
T[]=x; T[]=y;
if (f) res=-inf,quemx(rt); else res=inf,quemn(rt);
return res;
} int main(){
freopen("bzoj1941.in","r",stdin);
freopen("bzoj1941.out","w",stdout);
scanf("%d",&n);
rep(i,,n) scanf("%d%d",&p[i][],&p[i][]);
rt=build(,n,);
rep(i,,n) ans=min(ans,que(,p[i][],p[i][])-que(,p[i][],p[i][]));
printf("%d\n",ans);
return ;
}
2.BZOJ2648
#include<cstdio>
#include<cstring>
#include<algorithm>
#define rep(i,l,r) for (int i=l; i<=r; i++)
typedef long long ll;
using namespace std; const int N=,inf=;
int n,m,F,nd,rt,op,res; inline void cmin(int &x,int y){ if (y<x) x=y; }
inline void cmax(int &x,int y){ if (y>x) x=y; }
int abs(int x){ return (x<) ? -x : x; } struct P{
int d[],mn[],mx[],l,r;
int& operator [](int x){ return d[x]; }
friend bool operator <(P a,P b){ return a[F]<b[F]; }
friend int dis(P a,P b){ return abs(a[]-b[])+abs(a[]-b[]); }
}p[N],t[N],a; void upd(int x){
int l=t[x].l,r=t[x].r;
rep(i,,){
t[x].mn[i]=t[x].mx[i]=t[x][i];
if (l) cmin(t[x].mn[i],t[l].mn[i]),cmax(t[x].mx[i],t[l].mx[i]);
if (r) cmin(t[x].mn[i],t[r].mn[i]),cmax(t[x].mx[i],t[r].mx[i]);
}
} int build(int l,int r,int k){
if (l>r) return ;
F=k; int mid=(l+r)>>;
nth_element(p+l,p+mid,p+r+); t[mid]=p[mid];
rep(i,,) t[mid].mn[i]=t[mid].mx[i]=t[mid][i];
t[mid].l=build(l,mid-,k^); t[mid].r=build(mid+,r,k^);
upd(mid); return mid;
} void ins(int &x,int k){
if (!x) { t[x=++nd]=a; upd(x); return; }
if (a[k]<=t[x][k]) ins(t[x].l,k^); else ins(t[x].r,k^);
upd(x);
} int get(int x){
int s=;
rep(i,,) s+=max(a[i]-t[x].mx[i],)+max(t[x].mn[i]-a[i],);
return s;
} void que(int x){
res=min(res,dis(t[x],a));
int l=t[x].l,r=t[x].r,dl=inf,dr=inf;
if (l) dl=get(l); if (r) dr=get(r);
if (dl<dr) { if (dl<res) que(l); if (dr<res) que(r); }
else { if (dr<res) que(r); if (dl<res) que(l); }
} int main(){
scanf("%d%d",&n,&m);
rep(i,,n) scanf("%d%d",&p[i][],&p[i][]);
rt=build(,n,); nd=n;
rep(i,,m){
scanf("%d%d%d",&op,&a[],&a[]);
if (op==) ins(rt,); else res=inf,que(rt),printf("%d\n",res);
}
return ;
}
3.BZOJ4066
#include<cstdio>
#include<cstring>
#include<algorithm>
#define rep(i,l,r) for (int i=l; i<=r; i++)
#define G x1,y1,x2,y2,t[x].mn[0],t[x].mn[1],t[x].mx[0],t[x].mx[1]
typedef long long ll;
using namespace std; const int N=;
int n,op,F,rt,m=;
ll v,ans,cnt,x1,y1,x2,y2; inline void cmin(int &x,int y){ if (y<x) x=y; }
inline void cmax(int &x,int y){ if (y>x) x=y; } bool in(int x1,int y1,int x2,int y2,int X1,int Y1,int X2,int Y2)
{ return x1<=X1 && X2<=x2 && y1<=Y1 && Y2<=y2;}
bool out(int x1,int y1,int x2,int y2,int X1,int Y1,int X2,int Y2)
{ return x1>X2 || x2<X1 || y1>Y2 || y2<Y1; } struct P{
int d[],mn[],mx[],l,r,v; ll sm;
int& operator [](int x){ return d[x]; }
friend bool operator <(P a,P b){ return a[F]<b[F]; }
}T,p[N],t[N]; void upd(int x){
int l=t[x].l,r=t[x].r;
rep(i,,){
t[x].mn[i]=t[x].mx[i]=t[x][i];
if (l) cmin(t[x].mn[i],t[l].mn[i]),cmax(t[x].mx[i],t[l].mx[i]);
if (r) cmin(t[x].mn[i],t[r].mn[i]),cmax(t[x].mx[i],t[r].mx[i]);
}
t[x].sm=t[l].sm+t[r].sm+t[x].v;
} int build(int l,int r,int k){
if (l>r) return ;
F=k; int mid=(l+r)>>;
nth_element(p+l,p+mid,p+r+); t[mid]=p[mid];
t[mid].l=build(l,mid-,k^); t[mid].r=build(mid+,r,k^);
upd(mid); return mid;
} void ins(int &x,int k){
if (!x) { t[x=++cnt]=T; upd(x); return; }
if (T[]==t[x][] && T[]==t[x][]){ t[x].v+=T.v; t[x].sm+=T.v; return; }
if (T[k]<t[x][k]) ins(t[x].l,k^); else ins(t[x].r,k^);
upd(x);
} ll que(int x,int x1,int y1,int x2,int y2){
if (!x) return ; ll tmp=,l=t[x].l,r=t[x].r;
if (in(G)) return t[x].sm; if (out(G)) return ;
if (in(x1,y1,x2,y2,t[x][],t[x][],t[x][],t[x][])) tmp+=t[x].v;
tmp+=que(l,x1,y1,x2,y2)+que(r,x1,y1,x2,y2);
return tmp;
} int main(){
freopen("bzoj4066.in","r",stdin);
freopen("bzoj4066.out","w",stdout);
scanf("%d",&n);
while (){
scanf("%d",&op);
if (op==) break;
if (op==){
scanf("%lld%lld%lld",&x1,&y1,&v);
T[]=x1^ans; T[]=y1^ans; T.v=v^ans; ins(rt,);
if (cnt==m){
rep(i,,cnt) p[i]=t[i]; m+=; rt=build(,cnt,);
}
}else{
scanf("%lld%lld%lld%lld",&x1,&y1,&x2,&y2);
x1^=ans; y1^=ans; x2^=ans; y2^=ans;
printf("%lld\n",ans=que(rt,x1,y1,x2,y2));
}
}
return ;
}
4.崂山百花蛇草水:见LOJ代码
KD-Tree复习笔记(BZOJ1941 & BZOJ2648 & BZOJ4066)的更多相关文章
- k-d tree 学习笔记
以下是一些奇怪的链接有兴趣的可以看看: https://blog.sengxian.com/algorithms/k-dimensional-tree http://zgjkt.blog.uoj.ac ...
- K-D Tree学习笔记
用途 做各种二维三维四维偏序等等. 代替空间巨大的树套树. 数据较弱的时候水分. 思想 我们发现平衡树这种东西功能强大,然而只能做一维上的询问修改,显得美中不足. 于是我们尝试用平衡树的这种二叉树结构 ...
- kd tree学习笔记 (最近邻域查询)
https://zhuanlan.zhihu.com/p/22557068 http://blog.csdn.net/zhjchengfeng5/article/details/7855241 KD树 ...
- 【BZOJ-2648&2716】SJY摆棋子&天使玩偶 KD Tree
2648: SJY摆棋子 Time Limit: 20 Sec Memory Limit: 128 MBSubmit: 2459 Solved: 834[Submit][Status][Discu ...
- [学习笔记]K-D Tree
以前其实学过的但是不会拍扁重构--所以这几天学了一下 \(K-D\ Tree\) 的正确打开姿势. \(K\) 维 \(K-D\ Tree\) 的单次操作最坏时间复杂度为 \(O(k\times n^ ...
- BZOJ1941:[SDOI2010]Hide and Seek(K-D Tree)
Description 小猪iPig在PKU刚上完了无聊的猪性代数课,天资聪慧的iPig被这门对他来说无比简单的课弄得非常寂寞,为了消除寂寞感,他决定和他的好朋友giPi(鸡皮)玩一个更加寂寞的游戏- ...
- BZOJ4066:简单题(K-D Tree)
Description 你有一个N*N的棋盘,每个格子内有一个整数,初始时的时候全部为0,现在需要维护两种操作: 命令 参数限制 内容 1 x y A 1<=x,y<=N,A是正整数 ...
- BZOJ2648/2716:SJY摆棋子/[Violet]天使玩偶(K-D Tree)
Description 这天,SJY显得无聊.在家自己玩.在一个棋盘上,有N个黑色棋子.他每次要么放到棋盘上一个黑色棋子,要么放上一个白色棋子,如果是白色棋子,他会找出距离这个白色棋子最近的黑色棋子. ...
- luogu4169 [Violet]天使玩偶/SJY摆棋子 / bzoj2648 SJY摆棋子 k-d tree
k-d tree + 重构的思想,就能卡过luogu和bzoj啦orz #include <algorithm> #include <iostream> #include &l ...
随机推荐
- appium-在页面点击一下处理(一般处理提示蒙层)
在写用例的时候,经常会发现某些第一次进去的页面会有一个蒙层提示.我们只有处理掉这个蒙层,才能继续我们的用例: 这边我们使用的是TouchAction的tap方法 TouchAction action ...
- Leetcode 662.二叉树最大宽度
二叉树最大宽度 给定一个二叉树,编写一个函数来获取这个树的最大宽度.树的宽度是所有层中的最大宽度.这个二叉树与满二叉树(full binary tree)结构相同,但一些节点为空. 每一层的宽度被定义 ...
- winform 使用Anchor属性进行界面布局
每个控件的定位方法: 一.使用Anchor: Anchor分为Left.Top.Right.Bottom四个属性. 它们的含义如下: Top——表示控件中与父窗体(或父控件)相关的顶部应该保持固定. ...
- CodeForces A. Many Equal Substrings
http://codeforces.com/contest/1029/problem/A You are given a string tt consisting of nn lowercase La ...
- 用Margin还是用Padding?
用margin还是用padding这个问题是每个学习CSS进阶时的必经之路. CSS边距属性定义元素周围的空间.通过使用单独的属性,可以对上.右.下.左的外边距进行设置.也可以使用简写的外边距属性同时 ...
- WordPress多本小说主题–WNovel主题发布,十分钟搭建小说站! 现已更新至1.2版本
本文属于<WNovel主题操作手册>文章系列,该系列共包括以下 8 部分: WNovel主题使用手册之–主题安装及更新教程 WNovel主题使用手册之–小说管理 WNovel主题使用手册之 ...
- [bzoj] 3263 陌上花开 洛谷 P3810 三维偏序|| CDQ分治 && CDQ分治讲解
原题 定义一个点比另一个点大为当且仅当这个点的三个值分别大于等于另一个点的三个值.每比一个点大就为加一等级,求每个等级的点的数量. 显然的三维偏序问题,CDQ的板子题. CDQ分治: CDQ分治是一种 ...
- ss安装教程
https://teddysun.com/342.html 加速 wget –no-check-certificate https://github.com/teddysun/across/raw/m ...
- spring in action 学习笔记五:@Autowired这个注解如何理解
@Autowired这个注解的意思就是自动装配.他把一个bean对象自动装配到另一个对象中.下面的案例证明了spring的自动装配. 定义一个Sixi类.代码如下: package com.qls.a ...
- Nano
Nano命令指南 今天在输命令时,无意中输入了nano,对这个命令不太熟悉,结果不知道如何才能退出,保存,赶快查了一下资料,原来是这样的啊. 打开文件与新建文件 使用nano打开或新建文件,只需键入: ...