[BZOJ3064][Tyvj1518]CPU监控

16 篇文章 0 订阅
1 篇文章 0 订阅

题目大意

给定一个长度为 n 的序列{ai}。有 q 个操作,操作分如下几种:
 Q x y:询问序列上区间 [x,y] 的最大值
 A x y :询问序列上区间 [x,y] 的历史最大值
 P x y z :将序列上区间 [x,y] 的值都增加 z
 C x y z:将序列上区间 [x,y] 的值都修改成 z

1n105,1q105


题目分析

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


代码实现

标记处理代码比线段树长系列。

#include <iostream>
#include <climits>
#include <cstdio>
#include <cctype>

using namespace std;

int read()
{
    int x=0,f=1;
    char ch=getchar();
    while (!isdigit(ch)) f=ch=='-'?-1:f,ch=getchar();
    while (isdigit(ch)) x=x*10+ch-'0',ch=getchar();
    return x*f;
}

int buf[30];

void write(int x)
{
    if (x<0) putchar('-'),x=-x;
    for (;x;x/=10) buf[++buf[0]]=x%10;
    if (!buf[0]) buf[++buf[0]]=0;
    for (;buf[0];putchar('0'+buf[buf[0]--]));
}

const int INF=INT_MAX;
const int N=100050;

int a[N];
int n,q;

struct segment_tree
{
    int pre[N<<2][2],tag[N<<2][2];
    int h[N<<2],v[N<<2];

    void add_(int x,int delta)
    {
        h[x]=max(h[x],v[x]+delta);
        if (tag[x][1]!=-INF) pre[x][1]=max(pre[x][1],tag[x][1]+delta);
        else pre[x][0]=max(pre[x][0],tag[x][0]+delta);
    }

    void assign_(int x,int edit){h[x]=max(h[x],edit),pre[x][1]=max(pre[x][1],edit);}

    void add(int x,int delta)
    {
        h[x]=max(h[x],v[x]+=delta);
        if (tag[x][1]!=-INF) pre[x][1]=max(pre[x][1],tag[x][1]+=delta);
        else pre[x][0]=max(pre[x][0],tag[x][0]+=delta);
    }

    void assign(int x,int edit){h[x]=max(h[x],v[x]=edit),tag[x][1]=edit,pre[x][1]=max(pre[x][1],edit);}

    void clear(int x,int l,int r)
    {
        if (pre[x][0])
        {
            if (l!=r) add_(x<<1,pre[x][0]),add_(x<<1|1,pre[x][0]);
            pre[x][0]=0;
        }
        if (pre[x][1]!=-INF)
        {
            if (l!=r) assign_(x<<1,pre[x][1]),assign_(x<<1|1,pre[x][1]);
            pre[x][1]=-INF;
        }
        if (tag[x][0])
        {
            if (l!=r) add(x<<1,tag[x][0]),add(x<<1|1,tag[x][0]);
            tag[x][0]=0;
        }
        if (tag[x][1]!=-INF)
        {
            if (l!=r) assign(x<<1,tag[x][1]),assign(x<<1|1,tag[x][1]);
            tag[x][1]=-INF;
        }
    }

    void update(int x){v[x]=max(v[x<<1],v[x<<1|1]),h[x]=max(h[x<<1],h[x<<1|1]);}

    void modify(int x,int st,int en,int l,int r,int y,bool tp)
    {
        clear(x,l,r);
        if (st==l&&en==r)
        {
            if (tp) assign(x,y);
            else add(x,y);
            clear(x,l,r);
            return;
        }
        int mid=l+r>>1;
        if (en<=mid) modify(x<<1,st,en,l,mid,y,tp);
        else if (mid+1<=st) modify(x<<1|1,st,en,mid+1,r,y,tp);
        else modify(x<<1,st,mid,l,mid,y,tp),modify(x<<1|1,mid+1,en,mid+1,r,y,tp);
        update(x);
    }

    int query(int x,int st,int en,int l,int r,bool tp)
    {
        clear(x,l,r);
        if (st==l&&en==r) return tp?h[x]:v[x];
        int mid=l+r>>1;
        if (en<=mid) return query(x<<1,st,en,l,mid,tp);
        else if (mid+1<=st) return query(x<<1|1,st,en,mid+1,r,tp);
        else return max(query(x<<1,st,mid,l,mid,tp),query(x<<1|1,mid+1,en,mid+1,r,tp));
    }

    void build(int x,int l,int r)
    {
        pre[x][0]=tag[x][0]=0,pre[x][1]=tag[x][1]=-INF;
        if (l==r)
        {
            h[x]=v[x]=a[l];
            return;
        }
        int mid=l+r>>1;
        build(x<<1,l,mid),build(x<<1|1,mid+1,r),update(x);
    }
}t;

int main()
{
    freopen("monitor.in","r",stdin),freopen("monitor.out","w",stdout);
    n=read();
    for (int i=1;i<=n;++i) a[i]=read();
    t.build(1,1,n);
    for (q=read();q--;)
    {
        char opt=getchar();
        while (!isalpha(opt)) opt=getchar();
        int x=read(),y=read();
        if (opt=='Q'||opt=='A') write(t.query(1,x,y,1,n,opt=='A')),putchar('\n');
        else t.modify(1,x,y,1,n,read(),opt=='C');
    }
    fclose(stdin),fclose(stdout);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值