[BZOJ3064]Tyvj 1518 CPU监控(线段树)

题目描述

传送门

题解

这题真是一道恶心的线段树
需要维护的值有6个:当前最大值,当前的加法标记,当前的覆盖标记;历史最大值,历史最大的加法标记,历史最大的覆盖标记
其实说历史最大的加法和覆盖标记并不是那么准确,更准确地说应该是 现在能用来更新子树的最大值
维护当前的一系列量都比较好写,都是线段树的基本操作
但是维护历史的一系列量更新就比较麻烦了,首先要知道的是维护历史的量一定分别对应大于等于维护当前的量,具体地:
更新子树的历史最大值:考虑这个区间的历史加法和历史覆盖,以及子树的当前最大值
更新子树的历史加法:考虑这个区间的历史加法和子树的当前加法
更新子树的历史覆盖:考虑这个区间的当前覆盖和子树的历史覆盖
值得注意的是如果子树当前没有覆盖,那么可以直接更新加法,否则应该把加法加到覆盖上
还有,每到一层区间就直接下放标记,无论区间是否完全覆盖

代码还是可读的,下划线系列是历史

代码

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
using namespace std;
#define N 100005
#define LL long long

const LL inf=1e18;
int n,m,a[N];
LL maxn[N*4],add[N*4],cover[N*4],_maxn[N*4],_add[N*4],_cover[N*4];

void update(int now)
{
    maxn[now]=max(maxn[now<<1],maxn[now<<1|1]);
    _maxn[now]=max(_maxn[now<<1],_maxn[now<<1|1]);
}
void pushdown(int now)
{
    int son;
    for (int i=0;i<=1;++i)
    {
        son=now<<1|i;
        _maxn[son]=max(_maxn[son],max(_cover[now],maxn[son]+_add[now]));
        if (cover[son]==-inf) _add[son]=max(_add[son],add[son]+_add[now]);
        else _cover[son]=max(_cover[son],cover[son]+_add[now]);
        if (add[now])
        {
            if (cover[son]!=-inf) cover[son]+=add[now];
            else add[son]+=add[now];
            maxn[son]+=add[now];
        }
        if (cover[now]!=-inf)
        {
            maxn[son]=cover[son]=cover[now];
            add[son]=0;
        }
        _cover[son]=max(_cover[son],max(cover[son],_cover[now]));
        _add[son]=max(_add[son],add[son]);
    }
    add[now]=_add[now]=0;
    cover[now]=_cover[now]=-inf;
}
void build(int now,int l,int r)
{
    int mid=(l+r)>>1;
    add[now]=_add[now]=0;
    cover[now]=_cover[now]=-inf;
    if (l==r)
    {
        maxn[now]=(LL)a[l];
        _maxn[now]=(LL)a[l];
        return;
    }
    build(now<<1,l,mid);
    build(now<<1|1,mid+1,r);
    update(now);
}
void change(int now,int l,int r,int lr,int rr,LL x,int opt)
{
    if (l!=r) pushdown(now);
    int mid=(l+r)>>1;
    if (lr<=l&&r<=rr)
    {
        if (!opt) maxn[now]+=x,add[now]+=x,_add[now]+=x;
        else cover[now]=_cover[now]=maxn[now]=x;
        _maxn[now]=max(_maxn[now],maxn[now]);
        return;
    }
    if (lr<=mid) change(now<<1,l,mid,lr,rr,x,opt);
    if (mid+1<=rr) change(now<<1|1,mid+1,r,lr,rr,x,opt);
    update(now);
}
LL query(int now,int l,int r,int lr,int rr,int opt)
{
    if (l!=r) pushdown(now);
    int mid=(l+r)>>1;
    LL ans=-inf;
    if (lr<=l&&r<=rr) return (opt?_maxn[now]:maxn[now]);
    if (lr<=mid) ans=max(ans,query(now<<1,l,mid,lr,rr,opt));
    if (mid+1<=rr) ans=max(ans,query(now<<1|1,mid+1,r,lr,rr,opt));
    return ans;
}
int main()
{
    scanf("%d",&n);
    for (int i=1;i<=n;++i) scanf("%d",&a[i]);
    build(1,1,n);
    scanf("%d",&m);
    for (int i=1;i<=m;++i)
    {
        char opt=getchar();
        while (opt!='Q'&&opt!='A'&&opt!='P'&&opt!='C') opt=getchar();
        int l,r,x;LL ans;

        if (opt=='Q')
        {
            scanf("%d%d",&l,&r);
            ans=query(1,1,n,l,r,0);
            printf("%lld\n",ans);
        }
        else if (opt=='A')
        {
            scanf("%d%d",&l,&r);
            ans=query(1,1,n,l,r,1);
            printf("%lld\n",ans);
        }
        else if (opt=='P')
        {
            scanf("%d%d%d",&l,&r,&x);
            change(1,1,n,l,r,(LL)x,0);
        }
        else if (opt=='C')
        {
            scanf("%d%d%d",&l,&r,&x);
            change(1,1,n,l,r,(LL)x,1);
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值