线段树打标记--bzoj3064CPU监控

Description

Bob需要一个程序来监视CPU使用率。这是一个很繁琐的过程,为了让问题更加简单,Bob会慢慢列出今天会在用计算机时做什么事。
Bob会干很多事,除了跑暴力程序看视频之外,还会做出去玩玩和用鼠标乱点之类的事,甚至会一脚踢掉电源……这些事有的会让做这件事的这段时间内CPU使用率增加或减少一个值;有的事还会直接让CPU使用率变为一个值。
当然Bob会询问:在之前给出的事件影响下,CPU在某段时间内,使用率最高是多少。有时候Bob还会好奇地询问,在某段时间内CPU曾经的最高使用率是多少。
为了使计算精确,使用率不用百分比而用一个整数表示。
不保证Bob的事件列表出了莫名的问题,使得使用率为负………………

Input

第一行一个正整数T,表示Bob需要监视CPU的总时间。
然后第二行给出T个数表示在你的监视程序执行之前,Bob干的事让CPU在这段时间内每个时刻的使用率达已经达到了多少。
第三行给出一个数E,表示Bob需要做的事和询问的总数。
接下来E行每行表示给出一个询问或者列出一条事件:
Q X Y:询问从X到Y这段时间内CPU最高使用率
A X Y:询问从X到Y这段时间内之前列出的事件使CPU达到过的最高使用率
P X Y Z:列出一个事件这个事件使得从X到Y这段时间内CPU使用率增加Z
C X Y Z:列出一个事件这个事件使得从X到Y这段时间内CPU使用率变为Z
时间的单位为秒,使用率没有单位。
X和Y均为正整数(X<=Y),Z为一个整数。
从X到Y这段时间包含第X秒和第Y秒。
保证必要运算在有符号32位整数以内。

Output

对于每个询问,输出一行一个整数回答。

Sample Input

10

-62 -83 -9 -70 79 -78 -31 40 -18 -5

20

A 2 7

A 4 4

Q 4 4

P 2 2 -74

P 7 9 -71

P 7 10 -8

A 10 10

A 5 9

C 1 8 10

Q 6 6

Q 8 10

A 1 7

P 9 9 96

A 5 5

P 8 10 -53

P 6 6 5

A 10 10

A 4 4

Q 1 5

P 4 9 -69

Sample Output

79

-70

-70

-5

79

10

10

79

79

-5

10

10

HINT

数据分布如下:

第1、2个数据保证T和E均小于等于1000

第3、4个数据保证只有Q类询问

第5、6个数据保证只有C类事件

第7、8个数据保证只有P类事件

全部数据保证T和E均小于等于100000

这一波打标记的操作非常骚气啊
不仅维护最值,还要维护历史最值

参考吉如一的2016年国家集训队论文《区间最值操作与历史最值问题》,里面讲得很清楚了。
一个重要的思路是,在一个点没有被赋值操作影响时,只需要维护区间加标记,在一个点被赋值操作影响过之后,所有操作都可以看成赋值操作。
线段树每个点维护四个标记,两个是正常的区间加与赋值标记,还有两个,分别代表一个点在上一次标记下传至今最大的区间加标记和赋值标记。然后各种讨论合并。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ls k<<1
#define rs k<<1|1
#define maxn 500005
#define ll long long
using namespace std;
const ll inf=1e16;

struct S{
    ll mx,c,p,pmx,pc,pp;
}node[maxn];
int n,m;
ll a[maxn];
char ch[10];

inline void update(int k){
    node[k].mx=max(node[ls].mx,node[rs].mx);
    node[k].pmx=max(node[ls].pmx,node[rs].pmx);
}

inline void build(int k,int L,int R){
    node[k].p=node[k].pp=0;
    node[k].c=node[k].pc=-inf;
    if(L==R){
        node[k].mx=node[k].pmx=a[L];
        return;
    }
    int mid=(L+R)>>1;
    build(ls,L,mid); build(rs,mid+1,R);
    update(k);
}

inline void pushdown(int k){
    for(int i=0;i<=1;i++){
        int son=k*2+i;
        node[son].pmx=max(node[son].pmx,max(node[k].pc,node[k].pp+node[son].mx));
        if(node[son].pc==-inf) node[son].pp=max(node[son].pp,node[k].pp+node[son].p);
        else node[son].pc=max(node[son].pc,node[son].c+node[k].pp);
        if(node[k].p){
            if(node[son].c!=-inf) node[son].c+=node[k].p;
            else node[son].p+=node[k].p;
            node[son].mx+=node[k].p;
        }
        if(node[k].c!=-inf){
            node[son].c=node[son].mx=node[k].c;
            node[son].p=0;
        }
        node[son].pc=max(node[son].pc,max(node[k].pc,node[son].c));
        node[son].pp=max(node[son].pp,node[son].p);
    }
    node[k].c=node[k].pc=-inf;
    node[k].p=node[k].pp=0;
}

inline ll ask(int k,int l,int r,int x,int y){
    ll res=-inf;
    if(l<r) pushdown(k);
    if(x<=l && r<=y){
        if(ch[0]=='A') return node[k].pmx;
        else return node[k].mx;
    }
    int mid=(l+r)>>1;
    if(x<=mid) res=max(res,ask(ls,l,mid,x,y));
    if(mid<y) res=max(res,ask(rs,mid+1,r,x,y));
    return res;
}

inline void change(int k,int l,int r,int x,int y,int z){
    if(l<r) pushdown(k);
    if(x<=l && r<=y){
        if(ch[0]=='P'){
            node[k].p+=z;
            node[k].mx+=z;
            node[k].pp+=z;
        }
        else{
            node[k].c=z;
            node[k].mx=z;
            node[k].pc=z;
        }
        node[k].pmx=max(node[k].pmx,node[k].mx);
        return;
    }
    int mid=(l+r)>>1;
    if(x<=mid) change(ls,l,mid,x,y,z);
    if(mid<y) change(rs,mid+1,r,x,y,z);
    update(k);
}

inline ll rd(){
    char c=getchar(); ll f=1,x=0;
    while(c!='-' && (c>'9' || c<'0')) c=getchar();
    if(c=='-') f=-1,c=getchar();
    while(c<='9' && c>='0') x=x*10+c-'0',c=getchar();
    return x*f;
}

int main(){
    n=rd();
    for(int i=1;i<=n;i++) a[i]=rd();
    build(1,1,n);
    m=rd();
    while(m--){
        scanf("%s",ch);
        ll x,y,z;
        if(ch[0]=='Q' || ch[0]=='A'){
            x=rd(); y=rd();
            printf("%lld\n",ask(1,1,n,x,y));
        }
        else{
            x=rd(); y=rd(); z=rd();
            change(1,1,n,x,y,z);
        }
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值