bzoj3064/洛谷P4314 CPU监控 线段树

本文介绍了一种处理区间操作的高效算法,通过定义一种特殊标记(tag)来实现区间值的最大化更新。标记由数对(a,b)构成,并通过自定义的合并规则,实现了区间加减操作的有效管理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目分析

看到这道题,你一定想到要新开一款和历史有关的标记……
但是这个标记很不好合并……
所以就有一种独特的打标记法……
就是一个标记tag,它是一个数对(a,b)(a,b),表示这个区间内所有值都要变成max(v+a,b)max(v+a,b),这样如果是区间加,就打上(a,inf)(a,−inf)标记,是区间减就打上(inf,b)(−inf,b)标记……
这个标记的合并呢……比如说当前区间原有标记(a1,b1)(a1,b1),还要打上新标记(a2,b2)(a2,b2),那么区间每一个值就变成max(max(v+a1,b1)+a2,b2)max(max(v+a1,b1)+a2,b2),也就是把标记变成(a1+a2,max(b1+a2,b2))(a1+a2,max(b1+a2,b2))…..
然后新开一款历史版标记,记为ht,表示当前这个区间最值,还未被标记tag改变,可以用的把其更新到最大的历史标记……
现在我们重载运算符,加法就是标记合并,取max就是a和b两位都取max合成的一个新标记,则pushdown操作如下:

void work(int i,node t1,node t2) {
    ht[i]=max(ht[i],tag[i]+t2);//加在当前这些v上的历史标记们,增加了tag[i]+t2这个标记
    hv[i]=max(hv[i],t2.cal(v[i]));//用最优历史标记更新v[i]的结果加在hv[i]上
    tag[i]=tag[i]+t1,v[i]=t1.cal(v[i]);
}
void pd(int i) {
    work(i<<1,tag[i],ht[i]);
    work((i<<1)|1,tag[i],ht[i]);
    tag[i].clear(),ht[i].clear();
}

代码

#include<bits/stdc++.h>
using namespace std;
#define RI register int
int read() {
    int q=0,w=1;char ch=' ';
    while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();
    if(ch=='-') w=-1,ch=getchar();
    while(ch>='0'&&ch<='9') q=q*10+ch-'0',ch=getchar();
    return q*w;
}
const int N=100005,inf=0x3f3f3f3f;
struct node{
    int a,b;
    void clear() {a=0,b=-inf;}
    int cal(int x) {return max(x+a,b);}
}tag[N<<2],ht[N<<2];
node operator + (node x,node y)
    {return (node){max(-inf,x.a+y.a),max(x.b+y.a,y.b)};}
node max(node x,node y) {return (node){max(x.a,y.a),max(x.b,y.b)};}
int v[N<<2],hv[N<<2],n,m,a[N];
void work(int i,node t1,node t2) {
    ht[i]=max(ht[i],tag[i]+t2);
    hv[i]=max(hv[i],t2.cal(v[i]));
    tag[i]=tag[i]+t1,v[i]=t1.cal(v[i]);
}
void up(int i) {
    v[i]=max(v[i<<1],v[(i<<1)|1]);
    hv[i]=max(hv[i<<1],hv[(i<<1)|1]);
}
void pd(int i) {
    work(i<<1,tag[i],ht[i]);
    work((i<<1)|1,tag[i],ht[i]);
    tag[i].clear(),ht[i].clear();
}
void build(int s,int t,int i) {
    tag[i].clear(),ht[i].clear();
    if(s==t) {v[i]=hv[i]=a[s];return;}
    int mid=(s+t)>>1;
    build(s,mid,i<<1),build(mid+1,t,(i<<1)|1);
    up(i);
}
int query(int o,int l,int r,int s,int t,int i) {
    if(l<=s&&t<=r) return o?hv[i]:v[i];
    int mid=(s+t)>>1,re=-inf; pd(i);
    if(l<=mid) re=query(o,l,r,s,mid,i<<1);
    if(mid+1<=r) re=max(re,query(o,l,r,mid+1,t,(i<<1)|1));
    return re;
}
void chan(int l,int r,int s,int t,int i,node k) {
    if(l<=s&&t<=r) {work(i,k,k);return;}
    int mid=(s+t)>>1; pd(i);
    if(l<=mid) chan(l,r,s,mid,i<<1,k);
    if(mid+1<=r) chan(l,r,mid+1,t,(i<<1)|1,k);
    up(i);
}
int main()
{
    char ch[10];int x,y,z;
    n=read();
    for(RI i=1;i<=n;++i) a[i]=read();
    build(1,n,1);
    m=read();
    while(m--) {
        scanf("%s",ch),x=read(),y=read();
        if(ch[0]=='Q') printf("%d\n",query(0,x,y,1,n,1));
        else if(ch[0]=='A') printf("%d\n",query(1,x,y,1,n,1));
        else if(ch[0]=='P') chan(x,y,1,n,1,(node){read(),-inf});
        else chan(x,y,1,n,1,(node){-inf,read()});
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值