题目
[SP1557 GSS2] Can you answer these queries II
分析
思路很巧妙。
先离线所有询问,然后考虑从左到右依次加入每个数,然后考虑如何求目前一个区间的最大子段和:子段无非是两个端点,我们可以用一个数组维护其中一个端点确定时,另一个端点的最优位置,即
a
i
a_i
ai 表示目前以
i
i
i 为左端点的子段和的最大值,由于是动态加入的,所以
a
i
a_i
ai 实际记录的就是从
i
i
i 到“当前位置”的子段和的历史最大值。然后我们要知道的是左端点滑动时的子段和最大值,于是用线段树维护
a
a
a 数组的区间最大值即可!又因为是动态加入的,
a
a
a 数组在区间
[
l
,
r
]
[l, r]
[l,r] 的最大值就是以右端点不超过“当前位置”,左端点在
[
l
,
r
]
[l, r]
[l,r] 范围内的最大子段和!又由于是动态加入,所以消除重复数字的影响很简单:只修改该数字上一次出现的位置过后的区间内的
a
a
a 即可。
一次离线,同时解决了两个问题。
代码
实现需要维护当前值、当前值懒标记、历史最大值、历史最大值懒标记,PushDown 也有讲究。
#include <bits/stdc++.h>
int Read() {
int x = 0; bool f = false; char c = getchar();
while (c < '0' || c > '9')
f |= c == '-', c = getchar();
while (c >= '0' && c <= '9')
x = x * 10 + (c ^ 48), c = getchar();
return f ? -x : x;
}
typedef long long LL;
const int MAXN = 100000;
const int MAXQ = 100000;
const int MAXA = 100000;
int N, Q, A[MAXQ + 5];
struct Ask {
int l, r, i;
} R[MAXQ + 5];
#define lch (u << 1)
#define rch (u << 1 | 1)
struct Node {
LL cur, his, curtag, histag;
Node operator + (const Node &other) const {
return { std::max(cur, other.cur), std::max(his, other.his), 0, 0 };
}
} T[MAXN * 4 + 5];
inline void PushDown(const int &u) {
auto Down = [u](Node &c) {
c.his = std::max(c.his, c.cur + T[u].histag);
c.histag = std::max(c.histag, c.curtag + T[u].histag);
c.cur += T[u].curtag, c.curtag += T[u].curtag;
};
Down(T[lch]), Down(T[rch]);
T[u].curtag = T[u].histag = 0;
}
void Add(const int &u, const int &l, const int &r, const int &lft, const int &rgt, const int &val) {
if (lft <= l && r <= rgt) {
T[u].his = std::max(T[u].his, T[u].cur += val);
T[u].histag = std::max(T[u].histag, T[u].curtag += val);
return;
}
PushDown(u);
int mid = (l + r) >> 1;
if (lft <= mid) Add(lch, l, mid, lft, rgt, val);
if (mid + 1 <= rgt) Add(rch, mid + 1, r, lft, rgt, val);
T[u] = T[lch] + T[rch];
}
Node Query(const int &u, const int &l, const int &r, const int &lft, const int &rgt) {
if (lft <= l && r <= rgt)
return T[u];
PushDown(u);
int mid = (l + r) >> 1;
if (rgt <= mid) return Query(lch, l, mid, lft, rgt);
if (lft >= mid + 1) return Query(rch, mid + 1, r, lft, rgt);
return Query(lch, l, mid, lft, rgt) + Query(rch, mid + 1, r, lft, rgt);
}
LL Ans[MAXQ + 5];
int Pre[MAXA * 2 + 5];
int main() {
N = Read();
for (int i = 1; i <= N; i++)
A[i] = Read();
Q = Read();
for (int i = 1; i <= Q; i++)
R[i].l = Read(), R[i].r = Read(), R[i].i = i;
std::sort(R + 1, R + Q + 1, [](const Ask &i, const Ask &j) {
return i.r < j.r;
});
int j = 1;
for (int i = 1; i <= N; i++) {
Add(1, 1, N, Pre[A[i] + MAXA] + 1, i, A[i]);
Pre[A[i] + MAXA] = i;
while (j <= Q && R[j].r == i)
Ans[R[j].i] = Query(1, 1, N, R[j].l, R[j].r).his, j++;
}
for (int i = 1; i <= Q; i++)
printf("%lld\n", Ans[i]);
return 0;
}

博客围绕 [SP1557 GSS2] 题目展开,采用离线处理所有询问的方法,从左到右依次加入每个数。用数组维护以 i 为左端点的子段和最大值,通过线段树维护该数组区间最大值,解决最大子段和问题,同时还解决了消除重复数字影响的问题,并提及代码实现的要点。
453

被折叠的 条评论
为什么被折叠?



