一开始看不懂题解,看懂了题解之后觉得还是挺妙的。
好多题解里都提到了HH的项链,但是我觉得关系并不大啊……
先把所有询问离线下来按照右端点排序,按照询问的要求一个一个加入数字,怎么加入数字,我们设计一颗特别的线段树:
假设当前我们在$[1, r]$中已经加完了数字,那么线段树的叶子结点$l$表示$\sum_{i = l}^{r}a_i$,非叶子结点在左右儿子之间取个最大值就好了,然后我们发现这样子答案就是询问区间中曾经出现过的数的最大的一个值,我们只要把这个最大值顺便保留计算一下就好了。
考虑一下怎么添加数,对于一个下标$i$,只要在$[pre_i + 1, i]$中区间加$a_i$即可($pre_i$表示前一个$a_i$出现的位置)。
用两个标记维护当前最大值和历史最大值,实现起来有一些细节。
时间复杂度$O(nlogn)$。
Code:
#include <cstdio> #include <cstring> #include <algorithm> using namespace std; typedef long long ll; const int N = 1e5 + 5; const int Fix = 1e5; int n, qn, a[N], head[N << 1], pre[N]; struct Querys { int l, r, id; ll res; friend bool operator < (const Querys &x, const Querys &y) { return x.r < y.r; } } q[N]; inline void read(int &X) { X = 0; char ch = 0; int op = 1; for(; ch > '9' || ch < '0'; ch = getchar()) if(ch == '-') op = -1; for(; ch >= '0' && ch <= '9'; ch = getchar()) X = (X << 3) + (X << 1) + ch - 48; X *= op; } inline ll max(ll x, ll y) { return x > y ? x : y; } template <typename T> inline void chkMax(T &x, T y) { if(y > x) x = y; } namespace SegT { struct Node { ll mx, tag, his, htag; inline Node (ll nowMx = 0LL, ll nowTag = 0LL, ll nowHis = 0LL, ll nowHtag = 0LL) { mx = nowMx, tag = nowTag, his = nowHis, htag = nowHtag; } } s[N << 2]; #define lc p << 1 #define rc p << 1 | 1 #define mid ((l + r) >> 1) #define mx(p) s[p].mx #define tag(p) s[p].tag #define his(p) s[p].his #define htag(p) s[p].htag inline void up(int p) { mx(p) = max(mx(lc), mx(rc)); his(p) = max(his(lc), his(rc)); } inline void down(int p) { chkMax(his(lc), mx(lc) + htag(p)); chkMax(his(rc), mx(rc) + htag(p)); mx(lc) += tag(p), mx(rc) += tag(p); chkMax(htag(lc), tag(lc) + htag(p)); chkMax(htag(rc), tag(rc) + htag(p)); tag(lc) += tag(p), tag(rc) += tag(p); tag(p) = htag(p) = 0; } void modify(int p, int l, int r, int x, int y, ll v) { if(x <= l && y >= r) { mx(p) += v; chkMax(his(p), mx(p)); tag(p) += v; chkMax(htag(p), tag(p)); return; } down(p); if(x <= mid) modify(lc, l, mid, x, y, v); if(y > mid) modify(rc, mid + 1, r, x, y, v); up(p); } ll query(int p, int l, int r, int x, int y) { if(x <= l && y >= r) return s[p].his; down(p); ll res = 0; if(x <= mid) chkMax(res, query(lc, l, mid, x, y)); if(y > mid) chkMax(res, query(rc, mid + 1, r, x, y)); return res; } } using namespace SegT; int main() { read(n); for(int i = 1; i <= n; i++) { read(a[i]); pre[i] = head[a[i] + Fix]; head[a[i] + Fix] = i; } read(qn); for(int i = 1; i <= qn; i++) { read(q[i].l), read(q[i].r); q[i].id = i, q[i].res = 0LL; } sort(q + 1, q + 1 + qn); for(int j = 1, i = 1; i <= qn; i++) { for(; j <= q[i].r && j <= n; j++) modify(1, 1, n, pre[j] + 1, j, 1LL * a[j]); ll now = query(1, 1, n, q[i].l, q[i].r); q[q[i].id].res = now; } for(int i = 1; i <= qn; i++) printf("%lld\n", q[i].res); return 0; }