【LG4169】[Violet]天使玩偶/SJY摆棋子
【LG4169】[Violet]天使玩偶/SJY摆棋子
题面
bzoj权限题呀
题解
cdq分治
其实题目就是说
实时插入点,并且给定点\((x,y)\)
求\(min_{i=1}^{n}\)\({|x-x_i|+|y-y_i|}\)。
我们考虑\(cdq\)分治,如何做呢?
绝对值很丑,其实可以分别考虑右上、左上、左下、右下四个方向
就可以把式子变成这样
\(min_{i=1}^{n}\)\({(x-x_i)+(y-y_i)}\)
\(\Rightarrow\) \((x+y)-max(x_i+y_i)\)其中\(x_i\)、\(y_i\)分别小于\(x\)、\(y\)
然后这个可以用值域树状数组维护一个前缀\(max\)来做
而四个方向直接将点按照坐标轴对称过去即可
代码
常数大,要吸氧
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <climits>
using namespace std;
namespace IO {
const int BUFSIZE = 1 << 20;
char ibuf[BUFSIZE], *is = ibuf, *it = ibuf;
inline char gc() {
if (is == it) it = (is = ibuf) + fread(ibuf, 1, BUFSIZE, stdin);
return *is++;
}
}
inline int gi() {
register int data = 0, w = 1;
register char ch = 0;
while (ch != '-' && (ch > '9' || ch < '0')) ch = IO::gc();
if (ch == '-') w = -1 , ch = IO::gc();
while (ch >= '0' && ch <= '9') data = data * 10 + (ch ^ 48), ch = IO::gc();
return w * data;
}
inline void chkmin(int &x, int y) { if (x > y) x = y; }
inline void chkmax(int &x, int y) { if (x < y) x = y; }
#define MAX_V 1000005
#define MAX_N 300005
inline int lb(int x) { return x & -x; }
int c[MAX_V], Lx = 0, Ly = 0;
void clear(int x) { while (x <= Ly) c[x] = 0, x += lb(x); }
int qmax(int x) { int res = 0; while (x > 0) chkmax(res, c[x]), x -= lb(x); return res; }
void add(int x, int v) { while (x <= Ly) chkmax(c[x], v), x += lb(x); }
struct Query { int t, x, y; bool fl; } q[MAX_N << 1]; int A[MAX_N << 1];
inline bool cmp_t(Query a, Query b) { return a.t < b.t; }
inline bool cmp_x(Query a, Query b) { return (a.x == b.x) ? (a.y < b.y) : (a.x < b.x); }
void Div(int l, int r) {
if (l == r) return ;
int mid = (l + r) >> 1;
Div(l, mid); Div(mid + 1, r);
int j = l;
for (int i = mid + 1; i <= r; i++) {
if (q[i].fl) continue;
for ( ; j <= mid && q[j].x <= q[i].x; ++j)
if (q[j].fl) add(q[j].y, q[j].x + q[j].y);
int res = qmax(q[i].y); if (res) chkmin(A[q[i].t], q[i].x + q[i].y - res);
}
for (int i = l; i < j; i++) if (q[i].fl) clear(q[i].y);
inplace_merge(&q[l], &q[mid + 1], &q[r + 1], cmp_x);
}
int N, M;
void init() { sort(&q[1], &q[N + 1], cmp_t); }
int main () {
N = gi(), M = gi();
for (int i = 1; i <= N; i++) {
int x = gi() + 1, y = gi() + 1;
q[i] = (Query){i, x, y, 1};
chkmax(Lx, x), chkmax(Ly, y);
}
while (M--) {
int op = gi(), x = gi() + 1, y = gi() + 1;
++N, q[N] = (Query){N, x, y, op & 1};
chkmax(Lx, x), chkmax(Ly, y);
}
for (int i = 1; i <= N; i++) A[i] = INT_MAX; ++Lx, ++Ly;
Div(1, N);
for (int i = 1; i <= N; i++) q[i].x = Lx - q[i].x; init(); Div(1, N);
for (int i = 1; i <= N; i++) q[i].x = Lx - q[i].x;
for (int i = 1; i <= N; i++) q[i].y = Ly - q[i].y; init(); Div(1, N);
for (int i = 1; i <= N; i++) q[i].y = Ly - q[i].y;
for (int i = 1; i <= N; i++) q[i].x = Lx - q[i].x, q[i].y = Ly - q[i].y; init(); Div(1, N);
for (int i = 1; i <= N; i++)
if (A[i] != INT_MAX) printf("%d\n", A[i]);
return 0;
}