P6792 [SNOI2020] 区间和
do_it_tomorrow · · 题解
如果只有操作
如果有操作
为了防止我忘记怎么写吉司机,如果
考虑怎么为
对于这个经典的线段树合并操作,我们需要维护包含左端点的最大和
那么要计算
所以我们需要快速的确定其儿子的
如果
我们只需要记录这个区间中有多少最小值就可以快速计算了,自然的想到考虑什么时候其区间不会改变。
考虑下面一个情况,假设现在
进一步的,我们得到了右端点经过
需要注意即使修改操作并没有包含整个区间因为其只会增加区间和所以
假设紫色区间和红色区间的和为分别为
需要注意如果
那么我们可以维护区间中阈值的最小值,然后将 pushup 就行了。
考虑证明这样操作的复杂度的正确性,因为一次暴力的更改
于是我们就得到了
最后感谢 zzq 老师在很久以前的讲解,在视频里学习了思路。
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e5+5,inf=0x3f3f3f3f3f3f3f3f;
int n,q,a[N];
struct Seg{
struct node{
int v,ct;node(){v=-inf,ct=0;}node(int k,int V) {v=k,ct=V;}
friend bool operator<(node a,node b){return a.v<b.v;}
friend node operator+(node a,node b){return{a.v+b.v,a.ct+b.ct};}
void ADD(int k){v+=k*ct;}
};
struct Node{
int mn,smn,lazy,Min;node sum,pre,suf,ans;
void EMP(){mn=smn=Min=inf,sum=pre=suf=ans={0,0},lazy=-inf;}
void set(int V){mn=lazy=V,Min=smn=inf,sum=pre=suf=ans={V,1};}
void ck_mn(int k){if(mn!=k) sum.ct=pre.ct=suf.ct=ans.ct=0; }
}s[N<<2];
int G(node A,node B){if(B.ct<=A.ct) return inf;return (A.v-B.v)/(B.ct-A.ct);}
Node pushup(Node X,Node Y){
Node A;A.EMP();
A.mn=min(X.mn,Y.mn),A.smn=min(X.smn,Y.smn);
if(X.mn<Y.mn) A.smn=min(A.smn,Y.mn);
if(Y.mn<X.mn) A.smn=min(A.smn,X.mn);
X.ck_mn(A.mn),Y.ck_mn(A.mn),A.sum=X.sum+Y.sum;
A.ans=max({X.ans,Y.ans,X.suf+Y.pre});
A.pre=max(X.pre,X.sum+Y.pre),A.suf=max(Y.suf,Y.sum+X.suf);
A.Min=min({G(A.pre,X.sum+Y.pre),G(A.suf,Y.sum+X.suf)});
A.Min=min({A.Min,G(A.ans,X.ans),G(A.ans,Y.ans),G(A.ans,X.suf+Y.pre)});
A.Min=min({A.Min+A.mn,X.Min,Y.Min});
return A;
}
void PUTTAG(int k,int V){
s[k].sum.ADD(V-s[k].mn),s[k].ans.ADD(V-s[k].mn);
s[k].pre.ADD(V-s[k].mn),s[k].suf.ADD(V-s[k].mn);
s[k].mn=s[k].lazy=V;
}
void pushdown(int k,int l,int r){
int mid=(l+r)/2;
zkw(k*2,l,mid,s[k].lazy);
zkw(k*2+1,mid+1,r,s[k].lazy);
s[k].lazy=-inf;
}
void zkw(int k,int l,int r,int V){
if(s[k].mn>=V) return;
if(s[k].smn>V&&V<s[k].Min){
PUTTAG(k,V);
return;
}
s[k].lazy=V,pushdown(k,l,r);
s[k]=pushup(s[k*2],s[k*2+1]);
}
void build(int k,int l,int r){
s[k].EMP();
if(l==r){s[k].set(a[l]);return;}
int mid=(l+r)>>1;
build(k*2,l,mid);
build(k*2+1,mid+1,r);
s[k]=pushup(s[k*2],s[k*2+1]);
}
void change(int k,int l,int r,int x,int y,int V){
if(x<=l&&r<=y){
if(s[k].mn>=V) return;
if(s[k].smn>V&&V<s[k].Min){PUTTAG(k,V);return;}
}
if(r<x||y<l) return;
int mid=(l+r)/2;
pushdown(k,l,r);
change(k*2,l,mid,x,y,V);
change(k*2+1,mid+1,r,x,y,V);
s[k]=pushup(s[k*2],s[k*2+1]);
}
Node ask(int k,int l,int r,int x,int y){
if(x<=l&&r<=y) return s[k];
int mid=(l+r)/2;
pushdown(k,l,r);
if(y<=mid) return ask(k*2,l,mid,x,y);
if(x>=mid+1) return ask(k*2+1,mid+1,r,x,y);
return pushup(ask(k*2,l,mid,x,y),ask(k*2+1,mid+1,r,x,y));
}
}tr;
signed main(){
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin>>n>>q;
for(int i=1;i<=n;i++){
cin>>a[i];
}
tr.build(1,1,n);
for(int i=1,op,x,y,z;i<=q;i++){
cin>>op>>x>>y;
if(!op){
cin>>z;
tr.change(1,1,n,x,y,z);
}
else{
cout<<max(tr.ask(1,1,n,x,y).ans.v,0ll)<<'\n';
}
}
return 0;
}