hdu5306 Gorgeous Sequence(吉司机线段树)

题意:

给定长度为n的序列a,
m次操作,操作有三种:
(0 x y t):[x,y]的每个数a(i)=min(a(i),t)
(1 x y):计算[x,y]的区间最大值
(2 x y):计算[x,y]的区间和

数据范围:n,m<=1e6

解法:
因为既存在区间取min操作也存在区间和操作,
当对区间取min的时候是无法维护区间和的,因此无法用传统的laz线段树来做.

势能线段树/吉司机线段树:
维护以下标记:
1.区间和sum
2.区间最大值ma
3.区间严格次大值se
4.区间最大值的个数t

对于取min操作,假设现在要将[l,r]对x取min:
1.如果x>=ma,不需要操作,直接退出
2.如果se<x<ma,此时只会影响最大值,因此sum+=t*(x-ma),ma更新为x
3.如果x<=se,此时无法快速更新,那么分别递归左右子节点.

算法的总复杂度是O(mlogn).
证明就略了,jls是用势能分析的方法证明的.
code:
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int maxm=5e5+5;
int n,m;
struct Tree{
    ll sum[maxm<<2];
    ll ma[maxm<<2];
    ll se[maxm<<2];
    ll t[maxm<<2];
    inline void pp(int node){//pushup
        sum[node]=sum[node*2]+sum[node*2+1];
        ma[node]=max(ma[node*2],ma[node*2+1]);
        if(ma[node*2]==ma[node*2+1]){
            se[node]=max(se[node*2],se[node*2+1]);
            t[node]=t[node*2]+t[node*2+1];
        }else{
            se[node]=max(se[node*2],se[node*2+1]);
            se[node]=max(se[node],min(ma[node*2],ma[node*2+1]));//注意
            if(ma[node]==ma[node*2]){
                t[node]=t[node*2];
            }else{
                t[node]=t[node*2+1];
            }
        }
    }
    inline void pt(int node,int x){//pushtag
        if(x<ma[node]){
            sum[node]+=t[node]*(x-ma[node]);
            ma[node]=x;
        }
    }
    inline void pd(int node){//pushdown
        pt(node*2,ma[node]);
        pt(node*2+1,ma[node]);
    }
    void update(int st,int ed,int val,int l,int r,int node){
        if(val>ma[node])return ;
        if(st<=l&&ed>=r&&val>se[node]){
            pt(node,val);
            return ;
        }
        pd(node);
        int mid=(l+r)/2;
        if(st<=mid)update(st,ed,val,l,mid,node*2);
        if(ed>mid)update(st,ed,val,mid+1,r,node*2+1);
        pp(node);
    }
    ll ask_sum(int st,int ed,int l,int r,int node){
        if(st<=l&&ed>=r)return sum[node];
        pd(node);
        int mid=(l+r)/2;
        ll ans=0;
        if(st<=mid)ans+=ask_sum(st,ed,l,mid,node*2);
        if(ed>mid)ans+=ask_sum(st,ed,mid+1,r,node*2+1);
        return ans;
    }
    int ask_ma(int st,int ed,int l,int r,int node){
        if(st<=l&&ed>=r)return ma[node];
        pd(node);
        int mid=(l+r)/2;
        int ans=0;
        if(st<=mid)ans=max(ans,ask_ma(st,ed,l,mid,node*2));
        if(ed>mid)ans=max(ans,ask_ma(st,ed,mid+1,r,node*2+1));
        return ans;
    }
    void build(int l,int r,int node){
        if(l==r){
            scanf("%lld",&sum[node]);
            ma[node]=sum[node];
            se[node]=-1;
            t[node]=1;
            return ;
        }
        int mid=(l+r)/2;
        build(l,mid,node*2);
        build(mid+1,r,node*2+1);
        pp(node);
    }
}T;
signed main(){
    int TT;scanf("%d",&TT);
    while(TT--){
        scanf("%d%d",&n,&m);
        T.build(1,n,1);
        while(m--){
            int op;scanf("%d",&op);
            if(op==0){
                int l,r,x;scanf("%d%d%d",&l,&r,&x);
                T.update(l,r,x,1,n,1);
            }else if(op==1){
                int l,r;scanf("%d%d",&l,&r);
                int ans=T.ask_ma(l,r,1,n,1);
                printf("%d\n",ans);
            }else if(op==2){
                int l,r;scanf("%d%d",&l,&r);
                ll ans=T.ask_sum(l,r,1,n,1);
                printf("%lld\n",ans);
            }
        }
    }
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值