UOJ#164 V(线段树)

题目链接

思路:
             16年的论文题,主要是标记的作用:记 (a,b) ( a , b ) 为区间所有的数加上 a a 再对b max max 。那么标记 (a,b) ( a , b ) 和标记 (c,d) ( c , d ) 是可以合并的: (a+c,max(b+c,d)) ( a + c , max ( b + c , d ) ) 。那么线段树 push_down p u s h _ d o w n 操作的时候就可以这样合并,历史最大值也记录一个这样的标记,这样就可以查询单点最大值和单点历史最大值了。这题加上输入挂会更快。

#include<bits/stdc++.h>
typedef long long ll;
const int maxn = 5e5 + 10;
const ll INF = 1e15;
using namespace std;

ll a[maxn << 2], b[maxn << 2], fa[maxn << 2], fb[maxn << 2];
int n, m, T, kase = 1, inde = (maxn << 2) - 1;

inline void solve(int o, ll a1, ll b1, ll a2, ll b2) {
    fa[o] = max(fa[o], a[o] + a2);
    fb[o] = max(fb[o], max(b2, b[o] + a2));
    b[o] = max(b1, b[o] + a1);
    a[o] = max(a[o] + a1, -INF);
}

inline void push_down(int o) {
    solve(o << 1, a[o], b[o], fa[o], fb[o]);
    solve(o << 1 | 1, a[o], b[o], fa[o], fb[o]);
    a[o] = fa[o] = 0; b[o] = fb[o] = -INF;
}

inline void build(int o, int l, int r) {
    if(l == r) { cin >> a[o]; b[o] = -INF; fa[o] = a[o]; fb[o] = b[o]; return ; }
    int mid = (l + r) >> 1;
    build(o << 1, l, mid);
    build(o << 1 | 1, mid + 1, r);
}

void update(int o, int l, int r, int ql, int qr, int x, int flag) {
    if(l >= ql && r <= qr) {
        if(flag == 1) solve(o, x, -INF, x, -INF);
        else if(flag == 2) solve(o, -x, 0, -x, 0);
        else solve(o, -INF, x, -INF, x);
        return ;
    }
    int mid = (l + r) >> 1; push_down(o);
    if(ql <= mid) update(o << 1, l, mid, ql, qr, x, flag);
    if(qr > mid) update(o << 1 | 1, mid + 1, r, ql, qr, x, flag);
}

ll query(int o, int l, int r, int idx, int flag) {
    if(l == r && flag == 4) return max(a[o], b[o]);
    if(l == r && flag == 5) return max(fa[o], fb[o]);
    int mid = (l + r) >> 1; push_down(o);
    if(idx <= mid) return query(o << 1, l, mid, idx, flag);
    else return query(o << 1 | 1, mid + 1, r, idx, flag);
}

int main() {
    ios::sync_with_stdio(0);
    cin >> n >> m;
    build(1, 1, n);
    while(m--) {
        int op, l, r, x;
        cin >> op;
        if(op <= 3) cin >> l >> r >> x; else cin >> x;
        if(op <= 3) update(1, 1, n, l, r, x, op);
        else cout << query(1, 1, n, x, op) << endl;
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值