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

 

  

 

转载于:https://www.cnblogs.com/galaxies/p/bzoj2482.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值