SPOJ1557 GSS2

不知道第几次回顾了,每次回顾感觉都有新的收获

这题YYZ认为非常的简单,我们一起去%%%她吧(洛谷$id: 54050$)

 

题面

给出$n$个数,有$q$个询问,求最大子段和,注意相同的数只算一次

 

做法

考虑神奇的转化

定义区间$[l,r]$的最大子段和为$A(l, r)$

定义左端点在$l$,右端点在$[l,r]$之间的区间和(重复元素记一次)的最大值为$P(l, r)$

定义左端点在$l$,右端点为$r$的区间和(重复元素记一次)为$S(l,r)$

那么,我们有转化$A(l, r) = max(P(l, r), P(l + 1, r), ..., P(r, r))$

这是显然成立的,相当于根据子段和的左端点进行分类后求最大值

 

一个疯狂的想法

我们枚靠$r$

每次$r$往右扩展时,

如果我们能动态地维护$P(l,r)...P(r,r)$

那么,对于每个$r$而言,

只要我们能用一种能快速求出$max(P(l,r), P(l +1, r),...,P(r,r))$的数据结构

我们就能回答询问右端点是$r$的所有询问

 

理下思路:

1.给所有查询按右端点排序

2.枚举右端点

3.在枚举的途中动态地维护$P(l,r)...P(r,r)$

($P(l,r)...P(r,r)$对应区间$[l,r]$)

4.用线段树快速回答右端点是$r$的询问

 

由定义:$P(l,r) = max(S(l,l), S(l,l+1),S(l,l+2),.....,S(l,r))$

考虑当$r$从$i-1$移动到$i$时,有$P(l,i) = max(P(l, i - 1), S(l, i))$(由定义)

我们从这个式子考虑维护

记$val[i]$表示$i$位置的元素值

记$pre[i]$表示满足$val[i] = val[j]$和$j < i$的最大的$j$

对于$[1, pre[i]]$的$j$而言,由于重复元素不计数,有$S(j,i) = S(j, i - 1)$

即$P(j, i) = P(j, i - 1)$,因此,线段树维护时跳过这一段即可保证不重

对于$[pre[j] + 1, i]$的$j$而言,有$S(j, i) = S(j, i - 1) + val[j]$

 

我们只要维护$S(j, i)$和$P(j, i)$就可以了

但是,由于是区间修改,因此我们要考虑合理地设计标记来维护

(假设$r$枚举到$i$)

不妨设$tag[j]$为为了维护$S(j, i)$而生的懒惰标记

设$lazy[j]$为为了维护$P(j, i)$而生的懒惰标记

注意之间的相对顺序即可,具体看代码注释

 

#include <cstdio>
#include <iostream>
#include <algorithm>
#define rem template <typename re>
#define ls (p << 1)
#define rs (p << 1 | 1)
#define ll long long
#define sid 800005
using namespace std;

char RR[23456];
extern inline char gc() {
    static char *S = RR + 23333, *T = RR + 23333;
    if(S == T) S = RR, fread(RR, 1, 23333, stdin);
    return *S ++;
}
inline int read() {
    int p = 0, w = 1; char c = gc();
    while(c > '9' || c < '0') { if(c == '-') w = -1; c = gc(); }
    while(c >= '0' && c <= '9') { p = p * 10 + c - '0'; c = gc(); }
    return p * w;
}

int n, m, ret;
int val[sid], pre[sid];
ll ans[sid];

struct Seg {
    ll P, S, tag, lazy;
    //t[p].P -> max(P(l,i)...P(r,i))
    //t[p].S -> max(S(l,i)...S(r,i))
    //t[p].tag -> 给[l, r]的S未加的值
    //t[p].lazy -> 所有tag中最大的那个值
} tr[sid];

struct Question {
    int l, r, id;
    friend bool operator < (Question a, Question b) {
        return a.r < b.r;
    }
} q[sid];

void Pushdown(int p) {
    if(!tr[p].tag && !tr[p].lazy) return;
    tr[ls].P = max(tr[ls].P, tr[ls].S + tr[p].lazy);
    //取S最大值和lazy最大值相加绝对是最大值
    tr[ls].lazy = max(tr[ls].lazy, tr[ls].tag + tr[p].lazy);
    //更新lazy
    tr[ls].S += tr[p].tag; tr[ls].tag += tr[p].tag;
    //依照定义
    tr[rs].P = max(tr[rs].P, tr[rs].S + tr[p].lazy);
    tr[rs].lazy = max(tr[rs].lazy, tr[rs].tag + tr[p].lazy);
    tr[rs].S += tr[p].tag; tr[rs].tag += tr[p].tag;
    tr[p].tag = 0; tr[p].lazy = 0;
}

void Update(int p) {
    tr[p].S = max(tr[ls].S, tr[rs].S);
    tr[p].P = max(tr[ls].P, tr[rs].P);
}

void Modify(int p, int l, int r, int ml, int mr, int mc) {
    if(ml <= l && mr >= r) {
        tr[p].tag += mc; tr[p].S += mc;
        //对[l,r]中所有数+mc,则t[p].S += mc
        //自然t[p].tag += mc
        tr[p].lazy = max(tr[p].lazy, tr[p].tag);
        //定义
        tr[p].P = max(tr[p].S, tr[p].P);
        //定义
        return;
    }
    Pushdown(p);
    int mid = (l + r) >> 1;
    if(ml <= mid) Modify(p << 1, l, mid, ml, mr, mc);
    if(mr > mid) Modify(p << 1 | 1, mid + 1, r, ml, mr, mc);
    Update(p);
}

ll Query(int p, int l, int r, int ml, int mr) {
    if(ml <= l && mr >= r) return tr[p].P;
    Pushdown(p); ll ans = -100005;
    int mid = (l + r) >> 1;
    if(ml <= mid) ans = Query(p << 1, l, mid, ml, mr);
    if(mr > mid) ans = max(ans, Query(p << 1 | 1, mid + 1, r, ml, mr));
    return ans;
}

int main() {
    n = read(); 
    for(int i = 1; i <= n; i ++) val[i] = read();
    m = read();
    for(int i = 1; i <= m; i ++) 
    q[i].l = read(), q[i].r = read(), q[i].id = i;
    sort(q + 1, q + m + 1);

    int tail = 1;
    for(int i = 1; i <= n; i ++) {
        if(tail > m) break;
        int nv = val[i] + 100005; //注意负数
        Modify(1, 1, n, pre[nv] + 1, i, val[i]);
        pre[nv] = i;
        while(q[tail].r == i) ans[q[tail].id] = Query(1, 1, n, q[tail].l, i), tail ++;
    }
    for(int i = 1; i <= m; i ++) printf("%lld\n", ans[i]);
    return 0;
}

 

posted @ 2018-07-11 16:54  remoon  阅读(255)  评论(0编辑  收藏  举报