luogu P4169 [Violet]天使玩偶/SJY摆棋子
luogu P4169 [Violet]天使玩偶/SJY摆棋子
大意
给出一些点和询问,每次问离这个点最近的点的距离是多少(欧式距离)
可以动态加点
题解
考虑最近点在左下角的情况
设
(
x
B
,
y
B
)
为
(
x
A
,
y
A
)
在
左
下
角
的
最
近
点
设(x_B,y_B)为(x_A,y_A)在左下角的最近点
设(xB,yB)为(xA,yA)在左下角的最近点
则
∣
x
A
−
x
B
∣
−
∣
y
A
−
y
B
∣
=
(
x
A
+
y
A
)
−
(
x
B
+
y
B
)
则|x_A - x_B|-|y_A - y_B| = (x_A +y_A) - (x_B + y_B)
则∣xA−xB∣−∣yA−yB∣=(xA+yA)−(xB+yB)
然后用树状数组维护
x
+
y
x+y
x+y的最大值即可
加上时间一维就是很裸的三维偏序问题
然后对于左上,右上,右下的情况就把坐标轴转转就好了
按照这个思路写,然后T到飞起的代码
// luogu-judger-enable-o2
#include<bits/stdc++.h>
#define lowbit(x) (x & -x)
#define N 2000005
#define INF 2333333
using namespace std;
struct Q{
int x, y, z, opt;
}q[N];
int cmp(Q x, Q y){
return x.x < y.x;
}
int cmpp(Q x, Q y){
return x.z < y.z;
}
int tree[N], tot;
void update(int x, int y){//树状数组维护最大值
for(; x < N && y > tree[x]; x += lowbit(x)) tree[x] = y;
}
int query(int x){
int ret = - INF;
for(; x; x -= lowbit(x)) ret = max(ret, tree[x]);
return ret;
}
void clean(int x){
for(; x < N; x += lowbit(x)) tree[x] = - INF;
}
int ANS[N], del[N], n, m;
void cdq(int l, int r){//CDQ板子
if(l == r) return;
int mid = (l + r) >> 1;
cdq(l, mid), cdq(mid + 1, r);
sort(q + l, q + mid + 1, cmp), sort(q + mid + 1, q + r + 1, cmp);
int sz = 0, pos = l - 1;
for(int i = mid + 1; i <= r; i ++){
while(q[pos + 1].x <= q[i].x && pos < mid){
pos ++;
if(q[pos].opt == 1) update(q[pos].y, q[pos].x + q[pos].y), del[++ sz] = pos;
}
if(q[i].opt == 2) ANS[q[i].z] = min(ANS[q[i].z], q[i].x + q[i].y - query(q[i].y));
}
for(int i = 1; i <= sz; i ++) clean(q[del[i]].y);
}
int main(){
scanf("%d%d", &n, &m);
for(int i = 1; i <= n; i ++){
tot ++;
scanf("%d%d", &q[tot].x, &q[tot].y);
q[tot].x ++, q[tot].y ++;//坐标可能从0开始
q[tot].z = 0; q[tot].opt = 1;
}
for(int i = 1; i <= m; i ++){
tot ++;
scanf("%d%d%d", &q[tot].opt, &q[tot].x, &q[tot].y);
q[tot].x ++, q[tot].y ++;
q[tot].z = i;
}
for(int i = 0; i < N; i ++) tree[i] = - INF, ANS[i] = INF;
sort(q + 1, q + 1 + tot, cmpp);//先把时间这一维排序去掉
cdq(1, tot);
for(int i = 1; i <= tot; i ++){
int xx = q[i].x, yy = q[i].y;
q[i].x = 1000002 - yy, q[i].y = xx;//每次把坐标轴逆时针旋转90度
}
sort(q + 1, q + 1 + tot, cmpp);
cdq(1, tot);
for(int i = 1; i <= tot; i ++){
int xx = q[i].x, yy = q[i].y;
q[i].x = 1000002 - yy, q[i].y = xx;
}
sort(q + 1, q + 1 + tot, cmpp);
cdq(1, tot);
for(int i = 1; i <= tot; i ++){
int xx = q[i].x, yy = q[i].y;
q[i].x = 1000002 - yy, q[i].y = xx;
}
sort(q + 1, q + 1 + tot, cmpp);
cdq(1, tot);
for(int i = 1; i <= tot; i ++){
if(ANS[i] != INF) printf("%d\n", ANS[i]);
}
return 0;
}
然鹅这题卡常严重,这份代码只有64’
然后开始卡常(笑
主要分为几点
1、每次旋转再排序是没有必要的,直接拿一开始的覆盖一下然后转就好了
2、cdq里面不用sort,直接把两个有序序列O(len)合并就好了,可以用c++里的merge
3、把cmp改成直接重载运算符,会快很多(我也不知道为蛤
然后就可以很开心地卡过去了
// luogu-judger-enable-o2
#include<bits/stdc++.h>
#define lowbit(x) (x & -x)
#define N 1000005
#define INF 2333333
using namespace std;
struct Q{
int x, y, z, opt;
}q[N], a[N], b[N];
int operator < (Q x, Q y){//3、
return x.x < y.x;
}
int tree[N], tot;
void update(int x, int y){
for(; x < N && y > tree[x]; x += lowbit(x)) tree[x] = y;
}
int query(int x){
int ret = - INF;
for(; x; x -= lowbit(x)) ret = max(ret, tree[x]);
return ret;
}
void clean(int x){
for(; x < N; x += lowbit(x)) tree[x] = - INF;
}
int ANS[N], del[N], n, m;
void cdq(int l, int r){
if(l == r) return;
int mid = (l + r) >> 1;
cdq(l, mid), cdq(mid + 1, r);
int sz = 0, pos = l - 1;
for(int i = mid + 1; i <= r; i ++){
while(q[pos + 1].x <= q[i].x && pos < mid){
pos ++;
if(q[pos].opt == 1) update(q[pos].y, q[pos].x + q[pos].y), del[++ sz] = pos;
}
if(q[i].opt == 2) ANS[q[i].z] = min(ANS[q[i].z], q[i].x + q[i].y - query(q[i].y));
}
for(int i = 1; i <= sz; i ++) clean(q[del[i]].y);
merge(q + l, q + mid + 1, q + mid + 1, q + r + 1, b + l);//2、
for(int i = l; i <= r; i ++) q[i] = b[i];
}
int main(){
scanf("%d%d", &n, &m);
for(int i = 1; i <= n; i ++){
tot ++;
scanf("%d%d", &q[tot].x, &q[tot].y);
q[tot].x ++, q[tot].y ++;
q[tot].z = 0; q[tot].opt = 1;
}
for(int i = 1; i <= m; i ++){
tot ++;
scanf("%d%d%d", &q[tot].opt, &q[tot].x, &q[tot].y);
q[tot].x ++, q[tot].y ++;
q[tot].z = i;
}
for(int i = 1; i <= tot; i ++) a[i] = q[i];
for(int i = 0; i < N; i ++) tree[i] = - INF, ANS[i] = INF;
cdq(1, tot);
for(int i = 1; i <= tot; i ++) q[i] = a[i], q[i].x = 1000002 - a[i].y, q[i].y = a[i].x;//1、
cdq(1, tot);
for(int i = 1; i <= tot; i ++) q[i] = a[i], q[i].x = 1000002 - a[i].x, q[i].y = 1000002 - a[i].y;
cdq(1, tot);
for(int i = 1; i <= tot; i ++) q[i] = a[i], q[i].x = a[i].y, q[i].y = 1000002 - a[i].x;
cdq(1, tot);
for(int i = 1; i <= tot; i ++){
if(ANS[i] != INF) printf("%d\n", ANS[i]);
}
return 0;
}
这题真毒瘤棒
坑点
1、坐标貌似可以从0开始
2、毒瘤卡常