题目链接
思路:
16年的论文题,主要是标记的作用:记
(a,b)
(
a
,
b
)
为区间所有的数加上
a
a
再对取
max
max
。那么标记
(a,b)
(
a
,
b
)
和标记
(c,d)
(
c
,
d
)
是可以合并的:
(a+c,max(b+c,d))
(
a
+
c
,
max
(
b
+
c
,
d
)
)
。那么线段树
push_down
p
u
s
h
_
d
o
w
n
操作的时候就可以这样合并,历史最大值也记录一个这样的标记,这样就可以查询单点最大值和单点历史最大值了。这题加上输入挂会更快。
#include<bits/stdc++.h>
typedef long long ll;
const int maxn = 5e5 + 10;
const ll INF = 1e15;
using namespace std;
ll a[maxn << 2], b[maxn << 2], fa[maxn << 2], fb[maxn << 2];
int n, m, T, kase = 1, inde = (maxn << 2) - 1;
inline void solve(int o, ll a1, ll b1, ll a2, ll b2) {
fa[o] = max(fa[o], a[o] + a2);
fb[o] = max(fb[o], max(b2, b[o] + a2));
b[o] = max(b1, b[o] + a1);
a[o] = max(a[o] + a1, -INF);
}
inline void push_down(int o) {
solve(o << 1, a[o], b[o], fa[o], fb[o]);
solve(o << 1 | 1, a[o], b[o], fa[o], fb[o]);
a[o] = fa[o] = 0; b[o] = fb[o] = -INF;
}
inline void build(int o, int l, int r) {
if(l == r) { cin >> a[o]; b[o] = -INF; fa[o] = a[o]; fb[o] = b[o]; return ; }
int mid = (l + r) >> 1;
build(o << 1, l, mid);
build(o << 1 | 1, mid + 1, r);
}
void update(int o, int l, int r, int ql, int qr, int x, int flag) {
if(l >= ql && r <= qr) {
if(flag == 1) solve(o, x, -INF, x, -INF);
else if(flag == 2) solve(o, -x, 0, -x, 0);
else solve(o, -INF, x, -INF, x);
return ;
}
int mid = (l + r) >> 1; push_down(o);
if(ql <= mid) update(o << 1, l, mid, ql, qr, x, flag);
if(qr > mid) update(o << 1 | 1, mid + 1, r, ql, qr, x, flag);
}
ll query(int o, int l, int r, int idx, int flag) {
if(l == r && flag == 4) return max(a[o], b[o]);
if(l == r && flag == 5) return max(fa[o], fb[o]);
int mid = (l + r) >> 1; push_down(o);
if(idx <= mid) return query(o << 1, l, mid, idx, flag);
else return query(o << 1 | 1, mid + 1, r, idx, flag);
}
int main() {
ios::sync_with_stdio(0);
cin >> n >> m;
build(1, 1, n);
while(m--) {
int op, l, r, x;
cin >> op;
if(op <= 3) cin >> l >> r >> x; else cin >> x;
if(op <= 3) update(1, 1, n, l, r, x, op);
else cout << query(1, 1, n, x, op) << endl;
}
return 0;
}