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 ...
随机推荐
- 孤荷凌寒自学python第六十二天学习mongoDB的基本操作并进行简单封装1
孤荷凌寒自学python第六十二天学习mongoDB的基本操作并进行简单封装1 (完整学习过程屏幕记录视频地址在文末) 今天是学习mongoDB数据库的第八天. 今天开始学习mongoDB的简单操作, ...
- hp raid json
hp机器均已在装OS之前划好raid,统一规格为2*480G SSD, 12*4T SATA ,2*1.6T SSD,其中2*480G SSD做系统盘,划分raid1 已知disk controlle ...
- 容器基础(二): 使用Namespace进行边界隔离
Linux Namespace 容器技术可以认为是一种沙盒(sandbox), 为了实现沙盒/容器/应用间的隔离,就需要一种技术来对容器界定边界,从而让容器不至于互相干扰.当前使用的技术就是Names ...
- poj2388 更水
Who's in the Middle Time Limit: 1000MS Memory Limit: 65536K Total Submissions: 34974 Accepted: 2 ...
- 使用“\n\t”将多行字符串拼接起来
以前js拼接字符串有好多 \n \t 不使用ES6 使用"\n\t"将多行字符串拼接起来: var roadPoem = 'Then took the other, as just ...
- VMware 密匙
11.0版本 1F04Z-6D111-7Z029-AV0Q4-3AEH8 亲测可用
- LeetCode -- Sum Root to Leaf NNumbers
Related Links: Path Sum: http://www.cnblogs.com/little-YTMM/p/4529982.html Path Sum II: http://www.c ...
- Oracle SQL 疑难解析读书笔记(一 基础)
1.在语句中找到和消除空值 select first_name,last_name from hr.employees where commission_pct is null is null 和 i ...
- bzoj 1111 - 四进制的天平
Description 给定 1000的十进制数, 求 最小的 四幂拆分 方案 有多少种 Solution 先大除法 \(n\log_4(n)\)次取余转化为 四进制数. 然后从 低位 往 高位 \( ...
- Java I/O 笔记
1. Java常用I/O类概述 2. 文件I/O 你可以根据该文件是二进制文件还是文本文件来选择使用FileInputStream(FileOutputStream)或者FileReader(File ...