嘘~ 正在从服务器偷取页面 . . .

SP1557 GSS2 - Can you answer these queries II 题解


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;
}

参考文献

[1] https://www.luogu.com.cn/blog/zyxxs/solution-sp1557


文章作者: q779
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-ND 4.0 许可协议。转载请注明来源 q779 !
评论
  目录