【洛谷题解】P7706 「Wdsr-2.7」文文的摄影布置

本文介绍使用线段树解决一类包含单点修改与区间查询的序列问题。通过对原问题进行数学抽象,提出针对不同子树位置情况下的max(ψ)计算方法。并给出完整的AC代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

原题链接:P7706

更好的阅读体验:吾の博客

文文的摄影布置-题解

题意分析

简化题意就是给定两个序列 A , B A,B A,B,规定: ψ ( i , k ) = A i + A k − min ⁡ { B j } ( i < j < k ) \psi(i,k)=A_i+A_k-\min\left.\{B_j\right.\}(i<j<k) ψ(i,k)=Ai+Akmin{Bj}(i<j<k)。给定一个区间 ( l , r ) (l,r) (l,r) 求该区间内的 max ⁡ ( ψ ) \max(\psi) max(ψ)。两个序列的数值可能发生变化

这是典型的单点修改、区间查询问题,显而易见可以运用线段树解决。

维护信息-分类讨论

依题意,首先需要维护每个区间分别最大的 A i A_i Ai 和最小的 B j B_j Bj

ψ ( i , k ) = A i + A k − min ⁡ { B j } ( i < j < k ) \psi(i,k)=A_i+A_k-\min\left.\{B_j\right.\}(i<j<k) ψ(i,k)=Ai+Akmin{Bj}(i<j<k)

对于该式,想要求 max ⁡ ( ψ ) \max(\psi) max(ψ),就是求 max ⁡ ( A i + A k − min ⁡ { b j } ) \max(A_i+A_k-\min\left.\{b_j\right.\}) max(Ai+Akmin{bj}) i , j , k i,j,k i,j,k 的位置并不是一定的,于是我们考虑 i , j , k i,j,k i,j,k 的位置,并且考虑如何计算某种情况下的 max ⁡ ( ψ ) \max(\psi) max(ψ)

情况 1:都在左子树或都在右子树

这种情况好搞,就是在维护区间信息(做 push_up 操作)时取一下左右子树最大值就行。

情况 2:横跨左右子树

将原式可以拆成两个小式子 A i − B j ( i < j ) A_i-B_j(i<j) AiBj(i<j) A k − B j ( j < k ) A_k-B_j(j<k) AkBj(j<k),并且考虑 i , j , k i,j,k i,j,k 在不同子树下的两式最大值计算。横跨左右子树分 i , j i,j i,j 在左子树而 k k k 在右子树 i i i 在左子树而 j , k j,k j,k 在右子树两种情况。

对于前者情况,我们维护当前区间左子树最大的 A i A_i Ai 和右子树最小的 B j B_j Bj 的差;对于后者情况,我们维护当前区间右子树最大的 A k A_k Ak 和左子树最小的 B j B_j Bj 的差。

最后

最后,求 max ⁡ ( ψ ) \max(\psi) max(ψ),对于情况 2 的前者的式子再加上右子树最大的 A k A_k Ak;对于情况 2 的后者的式子再加上左区间最大的 A i A_i Ai,共两种情况。

对于情况 1 分别为左右子树的 max ⁡ ( ψ ) \max(\psi) max(ψ),共两种情况。

上述四种情况,取最大值即为当前区间的 max ⁡ ( ψ ) \max(\psi) max(ψ)

一些笔者的提醒和建议(也可能有点多余):

  • 维护信息时要注意一下顺序。

  • 建树时要把情况 2 中的两式最大值和 ψ \psi ψ 初始化为负无穷。

  • 考虑到这题会爆 int 但是我们维护的信息比较多,参考了一下几位大佬的写法,可以 define int 为 LL,主函数返回值类型写成 signed。

其他的,大家看代码吧。

AC代码

#include <iostream>
#include <cstdio>
#define int long long

using namespace std;

const int  N = 5e5 + 10, INF = -0x3f3f3f3f;

struct Segment
{
	int a, b;
}seg[N];
struct Node
{
	int l, r;
	int amax; //A的最大值 
	int bmin; //B的最小值 
	int lmax; //A_i - B_j且i < j的最大值 
	int rmax; //A_k - B_j且j < k的最大值 
	int tmax; //\psi 
}tr[N * 4];
int n, m;

void push_up(Node &u, Node &l, Node &r)
{
	u.amax = max(l.amax, r.amax);
	u.bmin = min(l.bmin, r.bmin);
	//对应Node节点的信息 
	u.lmax =  l.amax - r.bmin; //a_i - b_j
	u.rmax = r.amax - l.bmin; //a_k - b_j
	//再与左右子树取最大值
	u.lmax = max(u.lmax, max(l.lmax, r.lmax));
	u.rmax = max(u.rmax, max(l.rmax, r.rmax));
	//求ψ
	u.tmax = max(max(l.lmax + r.amax, r.rmax + l.amax), max(l.tmax, r.tmax)); 
}

void push_up(int u)
{
	push_up(tr[u], tr[u << 1], tr[u << 1 | 1]);
}

void build(int u, int l, int r)
{
	if (l == r)
	{
		tr[u] = {l, r, seg[r].a, seg[r].b, INF, INF, INF}; 
	}
	else
	{
		tr[u] = {l, r};
		int mid = l + r >> 1;
		build(u << 1, l, mid);
		build(u << 1 | 1, mid + 1, r);
		push_up(u);
	}
}

void modify(int u, int x, int v, int tag)
{
	if (tr[u].l == x && tr[u].r == x)
	{
		if (tag == 1) tr[u].amax = v;
		if (tag == 2) tr[u].bmin = v;
	}
	else
	{
		int mid = tr[u].l + tr[u].r >> 1;
		if (x <= mid) modify(u << 1, x, v, tag);
		else modify(u << 1 | 1, x, v, tag);
		push_up(u);
	}
}

Node query(int u, int l, int r)
{
	if (tr[u].l >= l && tr[u].r <= r) return tr[u];
	else
	{
		int mid = tr[u].l + tr[u].r >> 1;
		if (r <= mid) return query(u << 1, l, r);
		else if (l > mid) return query(u << 1 | 1, l, r);
		else
		{
			Node res;
			auto Left = query(u << 1, l, r);
			auto Right = query(u << 1 | 1, l, r);
			push_up(res, Left, Right);
			return res;
		}
	}
}

signed main()
{
	scanf("%lld%lld", &n, &m);
	for (int i = 1; i <= n; i++) scanf("%lld", &seg[i].a);
	for (int i = 1; i <= n; i++) scanf("%lld", &seg[i].b);
	
	build(1, 1, n);
	
	for (int i = 1; i <= m; i++)
	{
		int op, x, y;
		scanf("%lld%lld%lld", &op, &x, &y);
		if (op == 1 || op == 2) modify(1, x, y, op);
		else printf("%lld\n", query(1, x, y).tmax);
	}
	return 0;
	 
}

这是笔者在洛谷的第一份题解,若有错误欢迎各位指正,有问题大家可以在评论区探讨。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

oier_Asad.Chen

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值