SP1557 GSS2 - Can you answer these queries II 题解
题目链接:SP1557 GSS2 - Can you answer these queries II
题意:
给定一个长度为 $n$ 的序列 $a_i$ 。
$q$ 次询问,查询区间最大子段和,相同的数只算一次。选出的子段可以为空
数据范围:
$1\le n,q \le 10^5,~ |a_i| \le 10^5$ 。
真就加了一句话就比 GSS1 难了 $+\infty$ 倍(悲) 感觉这题是 GSS 系列第二难的题。
因为相同的数只能算一次,所以直接维护那 $4$ 个信息就不太行了。
想起来一道题叫 HH 的项链,这题的思想和那道题有一些相似。
考虑从左往右依次加入每个元素,记 $\mathtt{pre}(a_i)$ 表示 $a_i$ 上次出现的位置。
对于第 $i$ 个元素 $a_i$ ,它能产生贡献的范围为 $[\,\mathtt{pre}(a_i) + 1, i-1\,]$
任意 $l~(\mathtt{pre}(a_i) < l < i-1)$ ,我们把 $a_i$ 拼接上去,形成 $[l, i]$ 的串
注意到这是一个类似“区间拼接”的操作,不妨用线段树维护为区间加法的形式
具体地,线段树叶子结点 $u$ 表示以 $u$ 为起点的最大子串长。
一次区间加法可能让结点的值变大,也可能变小,因此我们需要维护一个历史最大值。
考虑离线处理询问。对于询问 $(j,i)$ ,我们在加入 $a_i$ 后立即查询 $j \le l \le r \le i$ 的历史最大值。
注意到如果 $l$ 固定时, $[l,r]$ 的历史最大值其实就是 $l$ 贡献的最大值,因为历史最大值是单调不降的。
因此 $l$ 固定时,直接选 $r=i$ 即可,则 $r$ 固定了,又可知 $l$ 越小,越可能产生大的贡献。
则询问其实就是 $[j,i]$ 的历史最大值(注意在 $a_i$ 加入后才能查询)
考虑维护历史最大值。具体可以参考 洛谷P5568 [SDOI2008] 校门外的区间 题解
这里简单讲一下,我们需要维护当前最大值、当前加法标记,历史最大值,历史最大加法标记。
为什么要维护历史最大加法标记呢?因为我们每次的修改是在标记上进行的,
所以下传信息的时候,那个标记不一定是最大的时候,或者说我们会不小心丢失历史的某些信息。
然后这题就没啦,不过这个离线的操作还是比较难想的
时间复杂度 $\mathcal{O}((n + q) \log n)$
代码:
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define INF 0x3f3f3f3f3f3f3f3f
#define DDD cout << "Line here is " << __LINE__ << '\n';
void up(int &x,int y) { x < y ? x = y : 0; }
void down(int &x,int y) { x > y ? x = y : 0; }
#define N ((int)(1e5+15))
#define M ((int)(2e6+15))
#define B 100000
#define ls(at) ((at) << 1)
#define rs(at) ((at) << 1 | 1)
int n,m,a[N],L[N],R[N],id[N],pre[M];
struct node { int mx1,mx2,tag1,tag2; } tr[N * 4],ans[N];
void push_up(int at)
{
up(tr[at].mx1 = tr[ls(at)].mx1, tr[rs(at)].mx1);
up(tr[at].mx2 = tr[ls(at)].mx2, tr[rs(at)].mx2);
}
node merge(node a, node b)
{ return {max(a.mx1, b.mx1), max(a.mx2, b.mx2), 0, 0}; }
void proc(int u, int f)
{
up(tr[u].mx2, tr[u].mx1 + tr[f].tag2);
up(tr[u].tag2, tr[u].tag1 + tr[f].tag2);
tr[u].mx1 += tr[f].tag1;
tr[u].tag1 += tr[f].tag1;
}
void push_down(int at)
{
proc(ls(at), at); proc(rs(at), at);
tr[at].tag1 = tr[at].tag2 = 0;
}
void update(int nl,int nr,int k,int l = 1,int r = n,int at = 1)
{
if(nl <= l && r <= nr)
{
tr[at].mx1 += k; up(tr[at].mx2, tr[at].mx1);
tr[at].tag1 += k; up(tr[at].tag2, tr[at].tag1);
return ;
}
push_down(at);
int mid = (l + r) >> 1;
if(nl <= mid) update(nl,nr,k,l,mid,ls(at));
if(nr > mid) update(nl,nr,k,mid+1,r,rs(at));
push_up(at);
return;
}
node query(int nl,int nr,int l = 1,int r = n,int at = 1)
{
if(nl <= l && r <= nr) return tr[at];
push_down(at); int mid = (l + r) >> 1;
if(nl > mid) return query(nl,nr,mid+1,r,rs(at));
if(nr <= mid) return query(nl,nr,l,mid,ls(at));
return merge(query(nl,nr,l,mid,ls(at)), query(nl,nr,mid+1,r,rs(at)));
}
signed main()
{
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
// freopen("check.in","r",stdin);
// freopen("check.out","w",stdout);
cin >> n;
for(int i=1; i<=n; i++) cin >> a[i];
cin >> m;
for(int i=1; i<=m; i++) { cin >> L[i] >> R[i]; id[i] = i; }
sort(id + 1, id + 1 + m, [](int a,int b) { return R[a] < R[b]; });
int j = 1;
for(int i=1; i<=n; i++)
{
update(pre[a[i] + B] + 1, i, a[i]);
for(pre[a[i] + B] = i; R[id[j]] == i && j <= m; j++)
ans[id[j]] = query(L[id[j]], R[id[j]]);
}
for(int i=1; i<=m; i++) cout << ans[i].mx2 << '\n';
return 0;
}
参考文献: