NC15162 小H的询问

题目链接

题目

题目描述

小H给你一个数组 a ,要求支持以下两种操作:

  1. 0 l r (1lrn),询问区间 [l,r] 中权值和最大的有效子区间的权值和,一个子区间被认为是有效的当且仅当这个子区间中没有两个相邻的偶数或者奇数。

  2. 1 x v (1xn,109v109) ,将 a[x] 的值修改为 v

输入描述

第一行读入两个正整数 n,m(1n,m105) 第二行读入 n 个整数,第 i 个表示 a[i](109a[i]109) 接下来 m 行,每行三个数表示操作,描述见题目描述。

输出描述

输出每个询问的答案。

示例1

输入

10 10
-9 -8 -8 -8 2 -7 -5 2 2 3
0 3 5
0 4 4
0 2 4
1 6 6
1 1 6
1 5 9
0 1 2
1 5 -8
0 2 4
1 3 -2

输出

2
-8
-8
6
-8

题解

知识点:线段树。

见到这种连续段最大值的,先维护三个信息,区间有效最大值 mx 、区间左端点开始的有效最大值 lmx 、区间右端点开始的有效最大值 rmx ,用以维护合并。

继续考虑,合并需要端点的奇偶性相反才可行,因此需要维护左端点奇偶性 lodd 、右端点奇偶性 rodd

同时,考虑到 lmx,rmx 合并时会出现跨越两段的情况,需要再维护一个区间权值和 sum 。当左右可跨越时,可以用 lmx 与左子区间 sum 加 右子区间 lmx 取最大值, rmx 同理。需要注意, sum 不能用 lmx,rmx 替代,因为权值是有正有负的, sum 不一定是 lmx,rmx ,必须多维护一个 sum

对于 sum ,我们还需要来判断整个区间是否为有效区间,从而判断 sum 是否可用。为了少设一个变量保存区间是否整个有效(当然设了也行,写起来容易点),我们设 sum 初值为极小值 1018 的表示无效值,更新时取 sum 与左右子区间的 sum 的和的最大值即可。显然,左右子区间存在一个无效值,则区间的值一定无效。

因此,区间信息要维护 mx,lmx,rmx,lodd,rodd,sum

合并时, lodd,rodd 直接继承即可,接下来分两步:

  1. mx 取左右子区间 mx 的最大值, lmx,rmx 直接继承。

  2. 若左子区间 rodd 和右子区间 lodd 不同,则在第一步的基础上考虑跨越两段的情况。

    mx 需要再与左子区间 rmx 加右子区间 lmx 的和取最大值。

    lmx,rmx 考虑特殊情况,已经在考虑维护信息的时候分析过了。

    sum 也分析过了。

修改时,直接修改即可,过程非常朴素,就不讲了。

另外,由于线段树结构的问题,我需要多加一个表示区间是否存在的 exist 以用来合并无效区间时特判,这个完全可以用先判断后递归避免,但是我懒23333。

时间复杂度 O((n+m)logn)

空间复杂度 O(n)

代码

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
struct T {
bool exist = 0; // 区间是否存在
ll mx = -1e18; // 有效区间最大值
ll sum = -1e18; // 区间权值和(区间权值和不能用 lmx,rmx 替代)
bool lodd = 0, rodd = 0; // 左/右端点的奇偶性
ll lmx = -1e18, rmx = -1e18; // 从左/右端点出发的最大值
friend T operator+(const T &a, const T &b) {
if (!a.exist) return b;
if (!b.exist) return a;
T x = T();
x.exist = 1;
x.mx = max(a.mx, b.mx);
x.lodd = a.lodd, x.rodd = b.rodd;
x.lmx = a.lmx, x.rmx = b.rmx;
if (a.rodd ^ b.lodd) {
x.mx = max(x.mx, a.rmx + b.lmx);
x.sum = max(x.sum, a.sum + b.sum); // 取最大值,防止溢出
x.lmx = max(x.lmx, a.sum + b.lmx);
x.rmx = max(x.rmx, a.rmx + b.sum);
}
return x;
}
};
struct F {
ll upd;
T operator()(const T &x) {
return{
1,
upd,
upd,
(bool)(upd % 2),(bool)(upd % 2),
upd,upd,
};
}
};
template<class T, class F>
class SegmentTree {
int n;
vector<T> node;
void update(int rt, int l, int r, int x, F f) {
if (r < x || x < l) return;
if (l == r) return node[rt] = f(node[rt]), void();
int mid = l + r >> 1;
update(rt << 1, l, mid, x, f);
update(rt << 1 | 1, mid + 1, r, x, f);
node[rt] = node[rt << 1] + node[rt << 1 | 1];
}
T query(int rt, int l, int r, int x, int y) {
if (r < x || y < l) return T();
if (x <= l && r <= y) return node[rt];
int mid = l + r >> 1;
return query(rt << 1, l, mid, x, y) + query(rt << 1 | 1, mid + 1, r, x, y);
}
public:
SegmentTree(const vector<T> &src) { init(src); }
void init(const vector<T> &src) {
assert(src.size() >= 2);
n = src.size() - 1;
node.assign(n << 2, T());
function<void(int, int, int)> build = [&](int rt, int l, int r) {
if (l == r) return node[rt] = src[l], void();
int mid = l + r >> 1;
build(rt << 1, l, mid);
build(rt << 1 | 1, mid + 1, r);
node[rt] = node[rt << 1] + node[rt << 1 | 1];
};
build(1, 1, n);
}
void update(int x, F f) { update(1, 1, n, x, f); }
T query(int x, int y) { return query(1, 1, n, x, y); }
};
int main() {
std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int n, m;
cin >> n >> m;
vector<T> a(n + 1);
for (int i = 1;i <= n;i++) {
int x;
cin >> x;
a[i] = {
1,
x,
x,
(bool)(x % 2),(bool)(x % 2),
x,x,
};
}
SegmentTree<T, F> sgt(a);
while (m--) {
int op;
cin >> op;
if (op == 0) {
int l, r;
cin >> l >> r;
cout << sgt.query(l, r).mx << '\n';
}
else {
int x, v;
cin >> x >> v;
sgt.update(x, { v });
}
}
return 0;
}
posted @   空白菌  阅读(23)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
点击右上角即可分享
微信分享提示