天使玩偶
Ayu 在七年前曾经收到过一个天使玩偶,当时她把它当作时间囊埋在了地下。而七年后 的今天,Ayu 却忘了她把天使玩偶埋在了哪里,所以她决定仅凭一点模糊的记忆来寻找它。
我们把 Ayu 生活的小镇看作一个二维平面坐标系,而 Ayu 会不定时地记起可能在某个点 (x, y) 埋下了天使玩偶;或者 Ayu 会询问你,假如她在 (x,y) ,那么她离近的天使玩偶可能埋下的地方有多远。
因为 Ayu 只会沿着平行坐标轴的方向来行动,所以在这个问题里我们定义两个点之间的距离为dist(A,B)=|Ax-Bx|+|Ay-By|。其中 Ax 表示点 A的横坐标,其余类似。
输入格式
第一行包含两个整数n和m ,在刚开始时,Ayu 已经知道有n个点可能埋着天使玩偶, 接下来 Ayu 要进行m 次操作
接下来n行,每行两个非负整数 (xi,yi),表示初始n个点的坐标。
再接下来m 行,每行三个非负整数 t,xi,yi。
如果t=1 ,则表示 Ayu 又回忆起了一个可能埋着玩偶的点 (xi,yi) 。
如果t=2 ,则表示 Ayu 询问如果她在点 (xi,yi) ,那么在已经回忆出来的点里,离她近的那个点有多远
n,m≤3∗105
x,y≤106
正解部分
先考虑没有修改操作的情况↓
给出若干点对 (x,y), 和若干询问 (xp,yp), 询问距离 (xp,yp) 最近的点离它的距离 .
即要求的是 i=1minn(∣xi−x∣+∣yi−y∣), 这个绝对值符号不好处理, 考虑分类讨论,
对于一个点 (x,y), 距离它较近的点只可能在它的四个方向: 左上, 右上, 左下, 右下,
逐一考虑怎么解决, 先看 左上 的情况,
化简绝对值符号得到 d=x−xi+y−yi=x+y−(xi+yi),
为了使得 d 更小, 需要使得 xi+yi 尽量大,
于是可以想到先把 询问 与 <stron> 按 xi 排序, 然后从左向右扫,</stron>
- 遇到 点 (xi,yi), 在以 yi 为下标的 线段树/树状数组 中更新 xi+yi 的最大值 .
- 遇到 询问 (x,y), 查询 [1,y] 的区间最大值即可 .
到这里已经解决了 左上 的问题, 其他三个方向同理 .
上方其实是一个二维偏序的问题, 排序 维护 x 的偏序, 线段树/树状数组 维护 y 的偏序 .
接下来考虑修改操作,
修改的操作无非就是加了一维时间偏序, 整个变为三位偏序, 没打过模板的可以看 这里 .
第一维时间排序维护, 第二维 x 归并维护, 第三维 y 树状数组 维护 .
实现部分
- 刚开始以为要对每个象限单独处理, 码量极大, 最后发现只需要将整个坐标按 x,y 倒过来即可 .
- 每次 cdq 分治前需要将肯定不是询问 左上 的点干掉, 否则会 T 飞 .
#include<bits/stdc++.h>
#define reg register
const int maxn = 1e6 + 5;
const int inf = 0x3f3f3f3f;
int N;
int M;
int tot;
int Lim;
int Max_x;
int Max_y;
int max_x;
int max_y;
int new_tot;
int node_cnt;
int Ans[maxn];
int read(){
char c;
int s = 0, flag = 1;
while((c=getchar()) && !isdigit(c))
if(c == '-'){ flag =-1, c = getchar(); break ; }
while(isdigit(c)) s = s*10 + c-'0', c = getchar();
return s * flag;
}
struct Node{ int x, y, opt, tim; } A[maxn], B[maxn], tmp[maxn];
struct Bit_t{
int v[maxn];
inline void clear(int k){ while(k <= max_y) v[k] = 0, k += k&-k; }
inline void update(int k, int x){ while(k<=max_y)v[k]=std::max(v[k],x),k+=k&-k; }
inline int Query(int k){ int s=0; while(k)s=std::max(s,v[k]),k-=k&-k; return s; }
} bit_t;
void Del(){
max_x = 0, max_y = 0; new_tot = 0;
for(reg int i = 1; i <= tot; i ++)
if(A[i].opt) max_x = std::max(max_x, A[i].x), max_y = std::max(max_y, A[i].y);
for(reg int i = 1; i <= tot; i ++)
if(A[i].x <= max_x && A[i].y <= max_y) tmp[++ new_tot] = A[i];
for(reg int i = 1; i <= new_tot; i ++) A[i] = tmp[i];
}
inline void merge_sort(int l, int r){
if(l >= r) return ;
int mid = l+r >> 1;
merge_sort(l, mid), merge_sort(mid+1, r);
int t1 = l, t2 = mid+1, t3 = l;
while(t1 <= mid && t2 <= r){
if(A[t1].x <= A[t2].x){
if(!A[t1].opt) bit_t.update(A[t1].y, A[t1].x+A[t1].y);
tmp[t3 ++] = A[t1 ++];
}else{
int res = bit_t.Query(A[t2].y);
if(res && A[t2].opt) Ans[A[t2].tim] = std::min(Ans[A[t2].tim], A[t2].x+A[t2].y-res);
tmp[t3 ++] = A[t2 ++];
}
}
while(t2 <= r){
int res = bit_t.Query(A[t2].y);
if(res && A[t2].opt) Ans[A[t2].tim] = std::min(Ans[A[t2].tim], A[t2].x+A[t2].y-res);
tmp[t3 ++] = A[t2 ++];
}
for(reg int i = l; i < t1; i ++) bit_t.clear(A[i].y);
while(t1 <= mid) tmp[t3 ++] = A[t1 ++];
for(reg int i = l; i <= r; i ++) A[i] = tmp[i];
}
int main(){
N = read(), M = read();
for(reg int i = 1; i <= N; i ++) A[i].x = read(), A[i].y = read(), A[i].tim = i;
tot = N;
for(reg int i = 1; i <= M; i ++){
tot ++;
int t = read();
A[tot].x = read(), A[tot].y = read();
A[tot].tim = tot, A[tot].opt = (t == 2);
}
for(reg int i = 1; i <= tot; i ++){
A[i].x ++, A[i].y ++, B[i] = A[i];
Max_x = std::max(Max_x, A[i].x);
Max_y = std::max(Max_y, A[i].y);
Ans[i] = inf;
}
Del(); merge_sort(1, new_tot);
for(reg int i = 1; i <= tot; i ++) A[i] = B[i], A[i].x = Max_x - A[i].x + 1;
Del(); merge_sort(1, new_tot);
for(reg int i = 1; i <= tot; i ++) A[i] = B[i], A[i].y = Max_y - A[i].y + 1;
Del(); merge_sort(1, new_tot);
for(reg int i = 1; i <= tot; i ++) A[i] = B[i], A[i].x = Max_x - A[i].x + 1, A[i].y = Max_y - A[i].y + 1;
Del(); merge_sort(1, new_tot);
for(reg int i = N+1; i <= tot; i ++) if(B[i].opt) printf("%d\n", Ans[i]);
return 0;
}