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) xAxByAyB=(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、毒瘤卡常

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值