zoukankan      html  css  js  c++  java
  • 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
    ", ans[i]);
        return 0;
    }
  • 相关阅读:
    如何给发票抬头增加页签
    记录激活SAP SMTP服务过程
    反射
    乱码问题
    使用idea的常用的技巧
    解决double的值相加的问题
    代理模式之静态代理
    foreach的真面目
    记录java的面试的每一个瞬间
    变量的经典
  • 原文地址:https://www.cnblogs.com/reverymoon/p/9295464.html
Copyright © 2011-2022 走看看