UOJ【清华集训2015】V

题目大意

初始给出n个数,a[1]~a[n]
有m个操作
1,把a[l]~a[r]加上x
2,把a[l]~a[r]减去x后和0取max
3,把a[l]~a[r]和x取max
4,输出a[x]
5,输出a[x]的历史最大值(即出现过的值的最大值)

数据范围,n,m<=5*10^5,x<=10^9

设计标记

令标记(a,b)表示对于x进行max(x+a,b)那么
操作1相当于(x,-inf)
2相当于(-x,0)
3相当于(0,x)

合并标记

如果一个数x先经过(a,b),再经过(c,d),会怎样呢?
x=max(max((a+x),b)+c,d)=max(a+c+x,max(b+c,d))
所以可以把标记合并为(a+c,max(b+c,d))

如何处理历史最大值呢?

相当于记录一个历史最大值标记,无论x的值为何,经过这个标记一定是历史最大值。
其实可以把一个标记看做一个一次函数和一个与x轴平行的函数取max,每次操作i记做标记fi,设Fi=f0~i合并后的标记
那历史最大值标记肯定是(max(Fi_a),max(Fi_b))
用线段树来维护这些标记,对于线段树上的一个节点,假如当前它存在有标记,他的儿子的标记的时间显然是比它要先的,否则它的标记肯定已经下传了,
它的儿子的历史最大值标记应该由它的历史最大标记和它的儿子的当前标记合并后再拿去更新(因为它的Fi=fj+1~i (j<i) ,而它的儿子的Fj=fj’+1~j (j<j) ,所以它历史最大值标记和它的儿子相比,缺少fj’+1~j,所以和Fj合并即可)
,当前标记应该要和它的当前标记合并.

代码

#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long 
#define inf 1e17
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fod(i,a,b)for(i=a;i>=b;i--)
#define min(a,b) (a<b?a:b)
#define max(a,b) (a>b?a:b)
using namespace std;
const int maxn=5e5+5;
int ww;char ch;
inline int read(){
    ww=0;
    for(ch=getchar();ch<'0'||ch>'9';) ch=getchar();
    for(;ch>='0'&&ch<='9';ch=getchar()) ww=ww*10+ch-48;
    return ww;
}
int i,j,n,m,x;ll a[maxn]; 
struct ar{
    ll a,b,ma,mb;
}tree[maxn*4],p,ze=(ar){0,-inf,0,-inf};
ar operator +=(ar &a,ar p){
    a.ma=max(a.ma,a.a+p.ma);
    a.mb=max(a.mb,max(a.b+p.ma,p.mb));
    a.a=max(a.a+p.a,-inf);
    a.b=max(a.b+p.a,p.b);
}
void down(int k){
    int lk=k<<1,rk=lk+1;
    tree[lk]+=tree[k],tree[rk]+=tree[k];
    tree[k]=ze;
}
void change(int k,int l,int r,int a,int b){
    if (l!=r)down(k);
    if (l==a&&r==b){
        tree[k]+=p;
        return;
    }
    int m=(l+r)>>1,lk=k<<1,rk=lk+1;
    if (b<=m) change(lk,l,m,a,b);else
    if (a>m) change(rk,m+1,r,a,b);else
    change(lk,l,m,a,m),change(rk,m+1,r,m+1,b);
}
void find(int k,int l,int r){
    if (l==r) {p=tree[k];return;}
    down(k);
    int m=(l+r)>>1,lk=k<<1,rk=lk+1;
    if (x<=m) find(lk,l,m);else find(rk,m+1,r);
}
int main(){
    n=read(),m=read();
    fo(i,1,n) a[i]=read();
    fo(i,1,4*n) tree[i]=ze;
    while (m--){
        int o=read(),l,r;
        if (o<=3){
            l=read(),r=read(),x=read();
            if (o==1) p=(ar){x,-inf,x,-inf};else
            if (o==2) p=(ar){-x,0,-x,0};else
            p=(ar){-inf,x,-inf,x};
            change(1,1,n,l,r);
        }else{
            x=read(),find(1,1,n);
            if (o==4) printf("%lld\n",max(a[x]+p.a,p.b));else
            printf("%lld\n",max(a[x]+p.ma,p.mb));
        }
    }
} 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值