bzoj2482 [Spoj1557] Can you answer these queries II
传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=2482
【题解】
经典的gss2。
考虑枚举区间右端点,线段树上每个节点存区间左端点的值(套路)
考虑加入一个右端点,只有$[pre_i+1, i]$会增加贡献$a_i$。
那么要考虑的就是:求右端点在$[l_i, r_i]$的时候,区间$[l_i, r_i]$的最大值,中的最大值。
容易想到离线,那么考虑按右端点排序然后依次统计答案。
线段树上记录当前值、当前tag;历史最大值,历史最大tag。
历史最大值和历史最大tag是和当前tag一起下传的,访问到即下传。
这个历史最大tag的含义有点歧义,我补充说明一下:
就是上一次下传标记到这一次下传这个区间的标记这么一段时间区间里,存在的tag的最大值。
注意到历史最大tag也是一个lazy标记,它随着当前tag一起下传,下传后清空。
那么显然原始数就是还没加tag的“当前值”,把原始数加上历史最大tag和历史最大值取max即可。
同理,由于$[l, r]$下传到的区间$[l, mid]$和$[mid+1, r]$这两个区间还没有进行标记下传。
所以历史最大tag也要相应更新。同理,如果这里存在当前tag值,上次下传到现在,都没有下传过标记。
当然,现在也还没有下传那两个子区间$[l, mid]$和$[mid+1, r]$的标记,所以可以用tag加上父亲的历史最大tag,来更新这个点的历史最大tag。
时间复杂度$O(nlogn)$
# include <stdio.h> # include <string.h> # include <iostream> # include <algorithm> // # include <bits/stdc++.h> # ifdef WIN32 # define LLFORMAT "%I64d" # else # define LLFORMAT "%lld" # endif using namespace std; typedef long long ll; typedef long double ld; typedef unsigned long long ull; const int N = 1e5 + 10; const int mod = 1e9+7; int n, m, a[N], lst[N + N], pre[N]; struct quest { int l, r, id; quest() {} quest(int l, int r, int id) : l(l), r(r), id(id) {} friend bool operator < (quest a, quest b) { return a.r < b.r; } }q[N]; const int SN = 262144 + 5; ll ans[N]; struct SMT { ll w[SN], tag[SN]; ll hw[SN], htag[SN]; // history # define ls (x<<1) # define rs (x<<1|1) inline void up(int x) { w[x] = max(w[ls], w[rs]); hw[x] = max(hw[ls], hw[rs]); } inline void pushtag(int x, ll tg, ll htg) { htag[x] = max(htag[x], tag[x] + htg); hw[x] = max(hw[x], w[x] + htg); w[x] += tg; tag[x] += tg; } inline void down(int x) { if(!tag[x] && !htag[x]) return ; pushtag(ls, tag[x], htag[x]); pushtag(rs, tag[x], htag[x]); tag[x] = htag[x] = 0; } inline void edt(int x, int l, int r, int L, int R, int d) { if(L <= l && r <= R) { pushtag(x, d, d); return ; } down(x); int mid = l+r>>1; if(L <= mid) edt(ls, l, mid, L, R, d); if(R > mid) edt(rs, mid+1, r, L, R, d); up(x); } inline ll query(int x, int l, int r, int L, int R) { if(L <= l && r <= R) return hw[x]; down(x); int mid = l+r>>1; if(R <= mid) return query(ls, l, mid, L, R); else if(L > mid) return query(rs, mid+1, r, L, R); else return max(query(ls, l, mid, L, mid), query(rs, mid+1, r, mid+1, R)); } # undef ls # undef rs }T; int main() { cin >> n; for (int i=1; i<=n; ++i) { scanf("%d", a+i); pre[i] = lst[a[i] + 100000]; lst[a[i] + 100000] = i; } cin >> m; for (int i=1; i<=m; ++i) scanf("%d%d", &q[i].l, &q[i].r), q[i].id = i; sort(q+1, q+m+1); for (int i=1, j=1; i<=n; ++i) { T.edt(1, 1, n, pre[i] + 1, i, a[i]); while(j <= m && q[j].r == i) ans[q[j].id] = T.query(1, 1, n, q[j].l, q[j].r), ++j; } for (int i=1; i<=m; ++i) printf(LLFORMAT "\n", ans[i]); return 0; }