题意:
给定长度为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;
}