F - Manhattan Christmas Tree 2

F 题思路
题目意思:

给你一堆圣诞树,每个圣诞树在一个点 (xi,yi)(x_i,y_i)(xi,yi) 下面给出 222 种操作

  • 单点修改 将第 iii 个圣诞树的点改为 (Xi,Yi)(X_i, Y_i)(Xi,Yi)
  • 区间查询 给定 (L,R,Xi,Yi)(L, R, X_i, Y_i)(L,R,Xi,Yi) 查询区间 (L,R)(L, R)(L,R) 中和 (Xi,Yi)(X_i,Y_i)(Xi,Yi) 曼哈顿距离最短的圣诞树。

其中,两个点 (x1,y1)(x_1,y_1)(x1,y1) 和 (x2,y2)(x_2,y_2)(x2,y2) 的曼哈顿距离为 ∣x1−x2∣+∣y1−y2∣|x_1-x_2|+|y_1-y_2|∣x1−x2∣+∣y1−y2∣。

解法

使用线段树,我们设 区间查询 中距离和 (x1,y1)(x_1,y_1)(x1,y1) 最短的圣诞树的坐标为 (x,y)(x, y)(x,y).

那么查询的答案为 ans=∣x1−x∣+∣y1−y∣ans=|x_1-x|+|y_1-y|ans=∣x1−x∣+∣y1−y∣.

这个东西在线段树上面是不好维护的,我们考虑拆贡献.

强行拆掉绝对值符号,那么
ans=max⁡{x1−x+y1−yx1−x−y1+y−x1+x+y1−y−x1+x−y1+y ans = \max \begin{cases} x_1-x+y_1-y \\ x_1-x-y_1+y \\ -x_1+x+y_1-y \\ -x_1+x-y_1+y \\ \end{cases} ans=max⎩ ⎨ ⎧x1−x+y1−yx1−x−y1+y−x1+x+y1−y−x1+x−y1+y

整理一下答案,得
ans=max⁡{(x1+y1)−(x+y)(x1−y1)−(x−y)−(x1−y1)+(x−y)−(x1+y1)+(x+y) ans = \max \begin{cases} (x_1+y_1)-(x+y) \\ (x_1-y_1)-(x-y) \\ -(x_1-y_1)+(x-y) \\ -(x_1+y_1)+(x+y) \\ \end{cases} ans=max⎩ ⎨ ⎧(x1+y1)−(x+y)(x1−y1)−(x−y)−(x1−y1)+(x−y)−(x1+y1)+(x+y)

设 Ai=x1+y1A_i = x_1+y_1Ai=x1+y1 和 Bi=x1−y1B_i = x_1-y_1Bi=x1−y1

那么答案变为
ans=max⁡{Ai−(x+y)Bi−(x−y)−Bi+(x−y)−Ai+(x+y) ans = \max \begin{cases} A_i-(x+y) \\ B_i-(x-y) \\ -B_i+(x-y) \\ -A_i+(x+y) \\ \end{cases} ans=max⎩ ⎨ ⎧Ai−(x+y)Bi−(x−y)−Bi+(x−y)−Ai+(x+y)

  • 第 111 个式子 Ai−(x+y)A_i-(x+y)Ai−(x+y), 发现 AiA_iAi 越大式子越大.
  • 第 222 个式子 Bi−(x−y)B_i-(x-y)Bi−(x−y), 发现 BiB_iBi 越大式子越大.
  • 第 333 个式子, 发现 BiB_iBi 越小式子越大
  • 第 444 个式子, 发现 AiA_iAi 越小式子越大

因为 (x+y)(x+y)(x+y) 和 (x−y)(x-y)(x−y) 是不变的

所以现在转换成了最大最小值查询问题, 可以使用线段树维护。

形式化的讲,现在具体需要维护的东西是:

  • 每棵圣诞树 (x+y),(x−y)(x+y),(x-y)(x+y),(x−y) 的最大值.
  • 每棵圣诞树 (x+y),(x−y)(x+y),(x-y)(x+y),(x−y) 的最小值.

所以直接是线段树模板题.

然后单次查询出来就是答案

Code\text{Code}Code
c 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

//#define DEBUG

#define int long long
#ifdef DEBUG
#define DBG printf
#else
#define DBG(...)
#endif
typedef struct {
	int MaxAdd;
	int MaxDiff;
	int MinAdd;
	int MinDiff;
}Node;

int max(int x, int y) {return x > y ? x : y;}
int min(int x, int y) {return x < y ? x : y;}
void swap(int* x, int* y) {int t = *x; *x = *y; *y = t;}
void copy(Node* x, Node y) {x->MaxAdd = y.MaxAdd, x->MaxDiff = y.MaxDiff, x->MinAdd = y.MinAdd, x->MinDiff = y.MinDiff;}
int InRange(int x, int y, int ck_val) {return (x <= ck_val && y >= ck_val);}
int IsRangeIns(int x1, int y1, int x2, int y2) {return !(y1 < x2 || x1 > y2);}
#define MAXN 400010
#define L (rt << 1)
#define R (rt << 1 | 1)
#define INF_MAX 0x3f3f3f3f3f3f3f3fLL
#define INF_MIN -INF_MAX

Node tree[MAXN << 2];		// 4 倍空间
int InputPos[MAXN][2];		// 输入的 X_i, Y_i

Node merge(Node p1, Node p2) {return (Node) {max(p1.MaxAdd, p2.MaxAdd), max(p1.MaxDiff, p2.MaxDiff), min(p1.MinAdd, p2.MinAdd), min(p1.MinDiff, p2.MinDiff)};}

void PushUp(int rt) {copy(&tree[rt], merge(tree[L], tree[R]));}
// 建树部分
void build(int rt, int uLeft, int uRight) {
	if (uLeft == uRight) {				// 叶子节点
		int X = InputPos[uLeft][0], Y = InputPos[uLeft][1];
		copy(&tree[rt], (Node) {X + Y, X - Y, X + Y, X - Y});
		return ;
	}
	int mid = (uLeft + uRight) >> 1;
	build(L, uLeft, mid), build(R, mid + 1, uRight);
	PushUp(rt);
}
// 修改部分
void update(int rt, int rtLeft, int rtRight, int uPos, int X, int Y) {
	if (rtLeft == rtRight) {			// 叶子节点
		copy(&tree[rt], (Node) {X + Y, X - Y, X + Y, X - Y});
		return ;
	}
	int mid = (rtLeft + rtRight) >> 1;
	if (uPos <= mid) update(L, rtLeft, mid, uPos, X, Y);
	if (uPos > mid) update(R, mid + 1, rtRight, uPos, X, Y);
	PushUp(rt);
}
// 查询部分
Node query(int rt, int rtLeft, int rtRight, int qLeft, int qRight) {
	if (qLeft <= rtLeft && rtRight <= qRight) return tree[rt];
	
	int mid = (rtLeft + rtRight) >> 1;
	Node ans = (Node) {INF_MIN, INF_MIN, INF_MAX, INF_MAX};
	
	if (qLeft <= mid) copy(&ans, query(L, rtLeft, mid, qLeft, qRight));
	if (qRight > mid) copy(&ans, merge(ans, query(R, mid + 1, rtRight, qLeft, qRight)));
	return ans;
}

int N, Q;

signed main()
{
//	freopen("in.txt", "r", stdin);
//	freopen("out.txt", "w", stdout);
	
	scanf("%lld %lld", &N, &Q);
	for (int i = 1; i <= N; i++) scanf("%lld %lld", &InputPos[i][0], &InputPos[i][1]);
	
	build(1, 1, N);
	DBG("Build Tree Time = %.3lfs", (clock() - p) * 1.0 / CLOCKS_PER_SEC);

	
	while (Q --) {
		int op;
		scanf("%lld", &op);
		
		if (op == 1) {
			int pos, X, Y;
			scanf("%lld %lld %lld", &pos, &X, &Y);
			
			update(1, 1, N, pos, X, Y);
		}
		else {
			int l, r, X, Y;
			scanf("%lld %lld %lld %lld", &l, &r, &X, &Y);
			
			Node ans;
			copy(&ans, query(1, 1, N, l, r));
			
			int res = -1e18;
			res = max(res, ans.MaxAdd - (X + Y));
			res = max(res, ans.MaxDiff - (X - Y));
			res = max(res, -ans.MinDiff + (X - Y));
			res = max(res, -ans.MinAdd + (X + Y));
			printf("%lld\n", res);
		}
	}
	return 0;
}
后记 and Update\text{后记 and Update}后记 and Update
  • 创建日期: 2025-12-22
相关推荐
生锈的键盘7 小时前
推荐算法实践:交叉特征的理解
算法
小龙报7 小时前
【51单片机】从 0 到 1 玩转 51 蜂鸣器:分清有源无源,轻松驱动它奏响新年旋律
c语言·数据结构·c++·stm32·单片机·嵌入式硬件·51单片机
dllxhcjla7 小时前
数据结构和算法
数据结构
乌萨奇也要立志学C++7 小时前
【洛谷】BFS 求解最短路:从马的遍历到迷宫问题的实战解析
算法·宽度优先
老鼠只爱大米7 小时前
LeetCode经典算法面试题 #46:全排列(回溯、交换、剪枝等五种实现方案详细解析)
算法·leetcode·剪枝·回溯·全排列·stj算法
Dovis(誓平步青云)7 小时前
《滑动窗口算法:从 “暴力遍历” 到 “线性高效” 的思维跃迁》
运维·服务器·数据库·算法
_OP_CHEN8 小时前
【算法基础篇】(五十七)线性代数之矩阵乘法从入门到实战:手撕模板 + 真题详解
线性代数·算法·矩阵·蓝桥杯·c/c++·矩阵乘法·acm/icpc
天天爱吃肉82188 小时前
【跨界封神|周杰伦×王传福(陶晶莹主持):音乐创作与新能源NVH测试,底层逻辑竟完全同源!(新人必看入行指南)】
python·嵌入式硬件·算法·汽车
im_AMBER8 小时前
Leetcode 114 链表中的下一个更大节点 | 删除排序链表中的重复元素 II
算法·leetcode
xhbaitxl8 小时前
算法学习day38-动态规划
学习·算法·动态规划