如果给你平面内一些点,让你求距离某一个指定点最近的点,应该怎么办呢?
O(n)遍历!
但是,在遍历的过程中,我们发现有一些点是永远无法更新答案的。
如果我们把这些点按照一定顺序整理起来,省略对不必要点的遍历,是不是可以降低时间复杂度呢?
这样的话,我们要用到的工具就是KDtree。
KDtree本质上是一颗BST(二叉搜索树),只不过每一层按照不同的维度分割,也就是说,一层划分x,一层划分y,交替进行。大概就是这样:
如果我们把他画在二维平面上的话,会发现KDtree实际上把一个矩形分割成了多个小矩形:
(我是来盗图的QAQ)
更新答案时,采用邻域搜索的方式。我们发现我们查询的点落在了某个小矩形内,我们用这个小矩形内的点去更新答案。然后进行回溯,看一下周围的矩形有没有可能存在更优答案,如果不可能的话,就不用搜索它了。
这样下来的复杂度最优是O(logn),随机数据介于O(logn)~O(sqrt(n))之间。如果是特意构造的数据,可以卡到O(n)(比如精度要求实数,给你一个圆,让你查询距离圆心最近的点)。
然而一般情况下KDtree比较好写,在考场上可以比较经济地拿到大部分分数,还是值得学习的。
关于代码实现,自己YY即可。反正我是脑补出来的。
例题:
BZOJ2716/2648:
KDtree查曼哈顿距离的板子,由于数据比较水所以直接插入可过,不需要考虑平衡性的问题。
代码自己YY。我的仅供参考(虽然我自行胡编的代码没什么参考价值QAQ)。
代码:
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #include<cstdlib> 6 using namespace std; 7 const int maxn=1.5e6+1e2; 8 const int inf=0x3f3f3f3f; 9 10 int cmp,ans; 11 struct Point { 12 int d[2]; 13 friend bool operator < (const Point &a,const Point &b) { 14 return a.d[cmp] < b.d[cmp]; 15 } 16 int dis(const Point &o) const { 17 int ret = 0; 18 for(int i=0;i<2;i++) 19 ret += abs( d[i] - o.d[i] ); 20 return ret; 21 } 22 }ps[maxn]; 23 24 int lson[maxn],rson[maxn],mi[maxn][2],mx[maxn][2]; 25 Point dv[maxn]; 26 int cnt; 27 28 inline void update(int pos) { 29 if( lson[pos] ) { 30 for(int i=0;i<2;i++) 31 mi[pos][i] = min( mi[pos][i] , mi[lson[pos]][i] ), 32 mx[pos][i] = max( mx[pos][i] , mx[lson[pos]][i] ); 33 } 34 if( rson[pos] ) { 35 for(int i=0;i<2;i++) 36 mi[pos][i] = min( mi[pos][i] , mi[rson[pos]][i] ), 37 mx[pos][i] = max( mx[pos][i] , mx[rson[pos]][i] ); 38 } 39 } 40 inline void fill(int pos,const Point &p) { 41 dv[pos] = p; 42 for(int i=0;i<2;i++) 43 mx[pos][i] = mi[pos][i] = p.d[i]; 44 } 45 inline void build(int pos,int pl,int pr,int dir) { 46 const int pmid = ( pl + pr ) >> 1; 47 cmp = dir; 48 nth_element(ps+pl,ps+pmid,ps+pr+1); 49 fill(pos,ps[pmid]); 50 if( pl < pmid ) build(lson[pos]=++cnt,pl,pmid-1,dir^1); 51 if( pr > pmid ) build(rson[pos]=++cnt,pmid+1,pr,dir^1); 52 update(pos); 53 } 54 inline void insert(int pos,Point np,int dir) { 55 cmp = dir; 56 if( np < dv[pos] ) { 57 if( lson[pos] ) insert(lson[pos],np,dir^1); 58 else { 59 lson[pos] = ++cnt; 60 fill(lson[pos],np); 61 } 62 } else { 63 if( rson[pos] ) insert(rson[pos],np,dir^1); 64 else { 65 rson[pos] = ++cnt; 66 fill(rson[pos],np); 67 } 68 } 69 update(pos); 70 } 71 inline int dis(int pos,const Point &p) { 72 int ret = 0; 73 for(int i=0;i<2;i++) 74 ret += max( p.d[i] - mx[pos][i] , 0 ) + max( mi[pos][i] - p.d[i] , 0 ); 75 return ret; 76 } 77 inline void query(int pos,const Point &p) { 78 ans = min( ans , p.dis(dv[pos]) ); 79 int dl = lson[pos] ? dis(lson[pos],p) : inf; 80 int dr = rson[pos] ? dis(rson[pos],p) : inf; 81 if( dl < dr ) { 82 if( dl < ans ) query(lson[pos],p); 83 if( dr < ans ) query(rson[pos],p); 84 } else { 85 if( dr < ans ) query(rson[pos],p); 86 if( dl < ans ) query(lson[pos],p); 87 } 88 } 89 90 int main() { 91 static int n,m; 92 static Point p; 93 94 scanf("%d%d",&n,&m); 95 for(int i=1;i<=n;i++) 96 scanf("%d%d",ps[i].d,ps[i].d+1); 97 98 build(cnt=1,1,n,0); 99 100 for(int i=1,t;i<=m;i++) { 101 scanf("%d%d%d",&t,p.d,p.d+1); 102 if(t == 1) { 103 insert(1,p,0); 104 } else { 105 ans = inf; 106 query(1,p); 107 printf("%d\n",ans); 108 } 109 } 110 return 0; 111 }