bzoj 3064: Tyvj 1518 CPU监控 (线段树)

题目描述

传送门

题目大意:给出一个长度为n的序列,有四种操作。
Q X Y:询问[x,y]的最大值
A X Y:询问[x,y]的历史最大值
P X Y Z:[x,y]中的每个数增加z
C X Y Z:[x,y]中的每个数都变成Z

题解

对于每个区间维护六个值。
tr 区间当前的最大值
mx 区间历史的最大值
cover 区间覆盖
add 区间增加
at 区间历史最大增量
ct 区间历史最大覆盖
因为我们要维护历史最大值,所以必须保证增量的标记,和最大的覆盖标记再未被别的标记覆盖之前及时的下放给下面的区间,所以我们在每次修改之前下放标记。
注意at,ct不是一直不清的,他更准确的说维护的是一段时间内的信息。
增加标记和覆盖标记是不能同时出现的,如果打增加标记的时候已经有cover标记,那么直接将增量给cover标记;如果打cover标记的时候有增加标记,那么就将增加标记归零。
在计算at,ct的时候需要结合上层的at,ct以及当前层的add,cover.
更具体的做法还是结合代码理解一下吧。

代码

#include<iostream>  
#include<cstdio>  
#include<cstring>  
#include<algorithm>  
#include<cmath>  
#define N 100003  
#define LL long long   
using namespace std;  
const LL inf=1e13;  
LL tr[N*4],a[N],mx[N*4],cover[N*4],add[N*4],ct[N*4],at[N*4];  
int n,m;  
void update(int now)  
{  
    tr[now]=max(tr[now<<1],tr[now<<1|1]);  
    mx[now]=max(mx[now<<1],mx[now<<1|1]); 
}  
void build(int now,int l,int r)  
{  
    cover[now]=-inf; ct[now]=-inf; 
    if (l==r) {  
        tr[now]=mx[now]=a[l];   
        return;  
    }  
    int mid=(l+r)/2;  
    build(now<<1,l,mid);  
    build(now<<1|1,mid+1,r);  
    update(now);  
}  

void pushdown(int now)  
{  
    for (int i=0;i<=1;i++) {
        int x=now<<1|i;
        mx[x]=max(mx[x],max(ct[now],tr[x]+at[now]));
        if (cover[x]==-inf) at[x]=max(at[x],add[x]+at[now]);
        else ct[x]=max(ct[x],cover[x]+at[now]);
        if (add[now]) {
            if (cover[x]!=-inf) cover[x]+=add[now];
            else add[x]+=add[now];
            tr[x]+=add[now];  
        }
        if (cover[now]!=-inf) {
            tr[x]=cover[x]=cover[now];
            add[x]=0;
        }
        ct[x]=max(ct[x],max(cover[x],ct[now]));
        at[x]=max(at[x],add[x]);
    }
    add[now]=at[now]=0;
    cover[now]=ct[now]=-inf;
}  
void qjadd(int now,int l,int r,int ll,int rr,LL val)  
{  
    if (l!=r) pushdown(now);
    if (ll<=l&&r<=rr) {  
        tr[now]+=val; add[now]+=val; at[now]+=val;
        mx[now]=max(mx[now],tr[now]);
        return;  
    }  
    int mid=(l+r)/2;  
    if (ll<=mid) qjadd(now<<1,l,mid,ll,rr,val);  
    if (rr>mid) qjadd(now<<1|1,mid+1,r,ll,rr,val);  
    update(now);  
}  
void qjcover(int now,int l,int r,int ll,int rr,LL val)  
{  
    if (l!=r) pushdown(now);
    if (ll<=l&&r<=rr) {  
        tr[now]=ct[now]=cover[now]=val;
        mx[now]=max(mx[now],tr[now]);
        return;
    }  
    int mid=(l+r)/2;  
    if (ll<=mid) qjcover(now<<1,l,mid,ll,rr,val);  
    if (rr>mid) qjcover(now<<1|1,mid+1,r,ll,rr,val);  
    update(now);  
}  
LL qjmax(int now,int l,int r,int ll,int rr,int opt)  
{  
    if (l!=r) pushdown(now);
    if (ll<=l&&r<=rr) {  
        if (opt==0) return mx[now];
        return tr[now];  
    }  
    int mid=(l+r)/2; LL ans=-inf;  
    if (ll<=mid) ans=max(ans,qjmax(now<<1,l,mid,ll,rr,opt));  
    if (rr>mid) ans=max(ans,qjmax(now<<1|1,mid+1,r,ll,rr,opt));  
    return ans;  
}  
int main()  
{  
    freopen("a.in","r",stdin);
    //freopen("my.out","w",stdout);
    scanf("%d",&n);  
    for (int i=1;i<=n;i++) scanf("%lld",&a[i]);  
    build(1,1,n);  
    scanf("%d",&m);  
    for (int i=1;i<=m;i++) {  
        char s[3]; int x,y; LL z;   
        scanf("%s%d%d",s+1,&x,&y);  
        if (s[1]=='A') printf("%lld\n",qjmax(1,1,n,x,y,0));  
        if (s[1]=='Q') printf("%lld\n",qjmax(1,1,n,x,y,1));  
        if (s[1]=='C') scanf("%lld",&z),qjcover(1,1,n,x,y,z);  
        if (s[1]=='P') scanf("%lld",&z),qjadd(1,1,n,x,y,z);  
    }  
}  
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值