先给出n个点, 然后有m个操作, (1, x, y) 表示查询离(x, y)最近点的曼哈顿距离, (2, x, y) 表示插入点 (x, y)。
这道题在线的话是可以用KD-Tree维护平面上的点集。但是既然题目没有要求在线那就用CDQ分治做好了。
CDQ分治是一种离线的分治算法。对于一个数据结构问题,无非存在两种操作:修改和查询。但是修改和查询往往都是动态的,也就是存在时间维度上的限制,要进行某一个查询,就一定要处理在这个查询之前的所有修改。而CDQ分治可以打破这一维的限制,使得动态查询变为静态查询。用到的方法就是对于一个询问,将它和所有和它有关的修改联系在一起。
(值得注意的是:这一维并不一定是时间上的限制,也有可能是别的。比如BZOJ3262 陌上花开。)
首先我们定义 Divide(L,R) ,表示处理完了区间 [L,R] 中修改对于查询的影响。首先我们引入分治的思想,将区间 [L,R] 分为 [L,mid] 和 [mid+1,R] ,首先处理 Divide(L,mid) ,然后处理 [L,mid] 中修改对于查询的影响,最后处理 Divide(mid+1,R) 。
可以看出, [mid+1,R] 中的修改并不会影响到 [L,mid] 中的询问,而在 [mid+1,R] 中的询问被 [L,mid] 和 [mid+1,R] (后者会继续分治将范围缩小)所共同影响。假设处理前半区间修改对后半区间查询的复杂度为 O(f(n)) 。那么总时间复杂度为 O(f(n)logn) 。也就是说,CDQ分治用了 O(logn) 的代价打破了时间维度的限制。
容易看出这道题的修改和查询操作分别为1和2。
我们要把和查询有关的修改整理成某个查询的前缀,在这道题里总共有三个限制 time , x , y 。其中 (x,y) 因为可以在查询点的任意方位不好搞,需要进行分类讨论,每次只考虑查询左上,右上,左下,右下之中的一个方位的最小曼哈顿距离,然后对于四次求出的答案中取 Min 即可。
考虑到编码方便
右上,左上和右下都可以由原平面水平和竖直翻转得到,只需要编码左下一个方位。
曼哈顿距离的计算方法为: dist((x,y),(x′,y′))=|x−x′|+|y−y′| ,当只考虑左下方位时可以去掉绝对值,原式转化为 dist((x,y),(x′,y′))=x+y−(y′+x′) 。即在询问 (x,y) 的所有修改前缀 (x′,y′) 中,我们要求 Max(x′+y′) 。
可以用数组数组维护区间最大值
Q:前面说到CDQ分治只能取消 time 一维的限制啊,还有 x 和 y 的限制怎么说?再套两个CDQ吗?
A:哈,不存在的, x 直接排序,树状数组插入下标用 y 。
翻转时要保证翻转后的点集 x>0,y>0 ,不然算 lowbit(x) 的时候直接死循环……
#include
const int Maxn = 1000010;
const int INF = 1 << 30;
using namespace std;
inline int Max(const int &a, const int &b) {
return a > b ? a : b;
}
inline int Min(const int &a, const int &b) {
return a < b ? a : b;
}
inline char get(void) {
static char buf[1000000], *p1 = buf, *p2 = buf;
if (p1 == p2) {
p2 = (p1 = buf) + fread(buf, 1, 1000000, stdin);
if (p1 == p2) return EOF;
}
return *p1++;
}
inline void read(int &x) {
x = 0; static char c; bool minus = false;
for (; !(c >= '0' && c <= '9'); c = get()) if (c == '-') minus = true;
for (; c >= '0' && c <= '9'; x = x * 10 + c - '0', c = get()); if (minus) x = -x;
}
struct Abcd {
int x, y, k, id;
friend bool operator < (const Abcd &a, const Abcd &b) {
if (a.x == b.x) return a.id < b.id;
else return a.x < b.x;
}
} oo[Maxn];
int n, m, Mx, ans[Maxn], t[Maxn];
inline void add(int x, int y) {
for (x; x <= Mx; x += x & -x) {
t[x] = Max(t[x], y);
}
}
inline int query(int x) {
int sum = 0;
for (; x; x -= x & -x) {
sum = Max(sum, t[x]);
}
return sum;
}
inline void clear(int x) {
for (x; x <= Mx; x += x & -x) {
t[x] = 0;
}
}
struct Cdq {
Abcd T[Maxn];
int n;
inline void init(const int &n) {
this->n = n;
sort(oo + 1, oo + 1 + n);
}
inline void solve(int L, int R) {
if (L >= R) return;
int mid = (L + R) >> 1, i = L, j = mid + 1;
for (int k = L; k <= R; k++) {
if (oo[k].id <= mid) T[i++] = oo[k];
else T[j++] = oo[k];
}
for (int k = L; k <= R; k++) oo[k] = T[k];
solve(L, mid);
i = L, j = mid + 1;
for (; j <= R; j++) if (oo[j].k == 2) {
for (; i <= mid && oo[i].x <= oo[j].x; i++) if (oo[i].k == 1) {
add(oo[i].y, oo[i].x + oo[i].y);
}
int tmp = query(oo[j].y);
if (tmp) ans[oo[j].id] = Min(ans[oo[j].id], oo[j].x + oo[j].y - tmp);
}
for (j = L; j < i; j++)
if (oo[j].k == 1) clear(oo[j].y);
solve(mid + 1, R);
merge(oo + L, oo + mid + 1, oo + mid + 1, oo + R + 1, T + L);
for (int i = L; i <= R; i++) oo[i] = T[i];
}
} cdq;
int main(void) {
//freopen("in.txt", "r", stdin);
//freopen("out.txt", "w", stdout);
read(n), read(m);
for (int i = 1; i <= n; i++) {
read(oo[i].x), read(oo[i].y);
Mx = Max(Mx, Max(++oo[i].x, ++oo[i].y));
oo[i].id = i;
oo[i].k = 1;
}
for (int i = n + 1; i <= n + m; i++) {
read(oo[i].k), read(oo[i].x), read(oo[i].y);
Mx = Max(Mx, Max(++oo[i].x, ++oo[i].y));
oo[i].id = i;
}
for (int i = 1; i <= n + m; i++) ans[i] = INF;
Mx++;
cdq.init(n + m);
cdq.solve(1, n + m);
for (int i = 1; i <= n + m; i++) oo[i].x = Mx - oo[i].x;
cdq.init(n + m);
cdq.solve(1, n + m);
for (int i = 1; i <= n + m; i++) oo[i].y = Mx - oo[i].y;
cdq.init(n + m);
cdq.solve(1, n + m);
for (int i = 1; i <= n + m; i++) oo[i].x = Mx - oo[i].x;
cdq.init(n + m);
cdq.solve(1, n + m);
for (int i = 1; i <= n + m; i++)
if (ans[i] != INF)
printf("%d\n", ans[i]);
return 0;
}
完。
By g1n0st