BZOJ2648 SJY摆棋子
题意:
这天,SJY显得无聊。在家自己玩。
在一个棋盘上,有N个黑色棋子。他每次要么放到棋盘上一个黑色棋子。要么放上一个白色棋子,假设是白色棋子,他会找出距离这个白色棋子近期的黑色棋子。
此处的距离是 曼哈顿距离 即(|x1-x2|+|y1-y2|) 。
如今给出N<=500000个初始棋子。和M<=500000个操作。对于每一个白色棋子,输出距离这个白色棋子近期的黑色棋子的距离。同一个格子可能有多个棋子。
思路:kdtree第一题。
kdtree是个什么东西呢?
比方如今有一些二维平面上的点,我们依照维度首先进行一次划分:
比方第一次划分x维。那么我们找到全部点中x坐标为中位数的点,将其作为根。x坐标小于中位数的点划分到左子树。否则划分到右子树。然后递归划分剩余的两側的点。下一次划分根据为y维。再下一次划分根据为x维。以此类推。
每一个节点存储的信息包括为以这个点为根的子树全部的点的矩形的左上角和右下角的坐标。我们在划分结束时更新就可以。
那么我们怎样查询到一个给定坐标(x,y)曼哈顿距离近期的点呢?
记录全局变量ans,表示眼下的最小距离。
初值设为INF.
从根节点開始,递归向下查询:首先用当前节点更新答案,随后看左右子树所相应的矩形,若询问点在矩形外,矩形距离的询问点到这个矩形的近期距离。否则为0.
我们优先选择矩形距离比較小的一側递归向下询问。
询问之后。若还有一側的矩形距离不大于当前的最优解,则再询问还有一側。
据神犇说这个过程是最坏O(sqrt(n))的。
考虑插入。
维护一下眼下划分的根据是哪一维。
从根開始递归向下插入。
首先用插入点更新当前节点,然后推断一下是应该插入到当前节点的左子树还是右子树,若插入的部分为空,直接插入。否则再递归插入。
那么还有最后一个问题。那就是kdtree的主体必须用指针实现。否则在大数组中的移动异常缓慢,查询和改动都要TLE.
Code:(指针版)
#include <cstdio> #include <cstring> #include <climits> #include <iostream> #include <algorithm> using namespace std; #define Min(a, b) ((a)<(b)?(a):(b)) #define Max(a, b) ((a)>(b)?(a):(b)) #define Abs(x) ((x)>0?(x):-(x)) #define N 500010 #define M 500010 int n, m; struct Point { int x, y; Point(int _x = 0, int _y = 0):x(_x),y(_y){} void set(int _, int __) { x = _, y = __; } }P[N + M]; int Dis(const Point &A, const Point &B) { return Abs(A.x - B.x) + Abs(A.y - B.y); } bool sign; inline bool cmp(const Point &A, const Point &B) { if (sign) return A.x < B.x || (A.x == B.x && A.y < B.y); else return A.y < B.y || (A.y == B.y && A.x < B.x); } struct Node { Node *l, *r; int x[2], y[2]; Point p; void SetP(const Point &P) { p = P; x[0] = x[1] = P.x; y[0] = y[1] = P.y; } int Dis(const Point &p) const { int res = 0; if (p.x < x[0] || p.x > x[1]) res += (p.x < x[0]) ?
x[0] - p.x : p.x - x[1]; if (p.y < y[0] || p.y > y[1]) res += (p.y < y[0]) ? y[0] - p.y : p.y - y[1]; return res; } void up(Node *B) { x[0] = Min(x[0], B->x[0]); x[1] = Max(x[1], B->x[1]); y[0] = Min(y[0], B->y[0]); y[1] = Max(y[1], B->y[1]); } }mem[N + M], *C = mem, Tnull, *null = &Tnull; Node *Build(int tl, int tr, bool d) { if (tl > tr) return null; int mid = (tl + tr) >> 1; sign = d; std::nth_element(P + tl + 1, P + mid + 1, P + tr + 1, cmp); Node *q = C++; q->SetP(P[mid]); q->l = Build(tl, mid - 1, d ^ 1); q->r = Build(mid + 1, tr, d ^ 1); if (q->l != null) q->up(q->l); if (q->r != null) q->up(q->r); return q; } #define INF 0x3f3f3f3f int res; void Ask(Node *q, const Point &p) { res = Min(res, Dis(q->p, p)); int DisL = q->l != null ? q->l->Dis(p) : INF; int DisR = q->r != null ? q->r->Dis(p) : INF; if (DisL < DisR) { if (q->l != null) Ask(q->l, p); if (DisR < res && q->r != null) Ask(q->r, p); } else { if (q->r != null) Ask(q->r, p); if (DisL < res && q->l != null) Ask(q->l, p); } } void Insert(Node *root, const Point &p) { Node *q = C++; q->l = q->r = null; q->SetP(p); sign = 0; while(1) { root->up(q); if (cmp(q->p, root->p)) { if (root->l == null) { root->l = q; break; } else root = root->l; } else { if (root->r == null) { root->r = q; break; } else root = root->r; } sign ^= 1; } } int main() { #ifndef ONLINE_JUDGE freopen("tt.in", "r", stdin); freopen("tt.out", "w", stdout); #endif scanf("%d%d", &n, &m); register int i; int ope, x, y; for(i = 1; i <= n; ++i) { scanf("%d%d", &x, &y); P[i] = Point(x, y); } Node* root = Build(1, n, 0); while(m--) { scanf("%d%d%d", &ope, &x, &y); if (ope == 1) Insert(root, Point(x, y)); else { res = INF; Ask(root, Point(x, y)); printf("%d\n", res); } } return 0; }