题解 P5693 【EI的第六分块】
首先,欢迎加入EI队长粉丝裙:450567214
本题的一个
该做法的原理在 EI's blog 或论文中有详细阐述,这里仅给出一个直接实现及可参考的代码.
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int N=400500;
const long long INF=2e18;
int n,w[N],m;
long long tag[N<<2];
struct LINE//一次函数
{
int k;long long b;
LINE operator +(const LINE &a)const{return (LINE){k+a.k,b+a.b};}
};
pair<LINE,long long> max(LINE a,LINE b)//对两个一次函数取x=0时的最值,同时给出max的选取不发生变化的最大值C(即 x>C 时max变成另一个)
{
if(a.k<b.k||(a.k==b.k&&a.b<b.b))swap(a,b);
if(a.b>=b.b){return make_pair(a,INF);}
return make_pair(b,(b.b-a.b)/(a.k-b.k));
}
struct Node
{
LINE ls,rs,ans,sum;long long x;//增量>x时子树内会有max的选取发生变化
Node operator +(const Node &a)const
{
Node t;t.x=min(x,a.x);
pair<LINE,long long>tmp=max(ls,sum+a.ls);
t.ls=tmp.first,t.x=min(t.x,tmp.second);
tmp=max(a.rs,rs+a.sum);
t.rs=tmp.first,t.x=min(t.x,tmp.second);
tmp=max(ans,a.ans);t.x=min(t.x,tmp.second);
tmp=max(tmp.first,rs+a.ls);
t.ans=tmp.first,t.x=min(t.x,tmp.second);
t.sum=sum+a.sum;
return t;
}
}a[N<<2];
void build(int rot,int lt,int rt)
{
if(lt==rt)
{
LINE t={1,w[lt]};
a[rot]=(Node){t,t,t,t,INF};
return;
}
int mid=(lt+rt)>>1;
build(rot<<1,lt,mid),build(rot<<1|1,mid+1,rt);
a[rot]=a[rot<<1]+a[rot<<1|1];
}
void upd(int rot,long long w)
{
tag[rot]+=w;a[rot].x-=w;
a[rot].ls.b+=a[rot].ls.k*w;
a[rot].rs.b+=a[rot].rs.k*w;
a[rot].ans.b+=a[rot].ans.k*w;
a[rot].sum.b+=a[rot].sum.k*w;
}
void upd(int rot,int lt,int rt,long long w)
{
if(w>a[rot].x)//如果增量使得子树内有max发生变化则递归下去重构
{
long long t=tag[rot]+w;tag[rot]=0;int mid=(lt+rt)>>1;
upd(rot<<1,lt,mid,t),upd(rot<<1|1,mid+1,rt,t);
a[rot]=a[rot<<1]+a[rot<<1|1];
}
else upd(rot,w);
}
void pushdown(int rot)
{
if(tag[rot])
{
long long t=tag[rot];tag[rot]=0;
upd(rot<<1,t),upd(rot<<1|1,t);
}
}
void update(int rot,int lt,int rt,int lq,int rq,int x)
{
if(lt>=lq&&rt<=rq){upd(rot,lt,rt,x);return;}
pushdown(rot);
int mid=(lt+rt)>>1;
if(lq<=mid)update(rot<<1,lt,mid,lq,rq,x);
if(rq>mid)update(rot<<1|1,mid+1,rt,lq,rq,x);
a[rot]=a[rot<<1]+a[rot<<1|1];
}
Node query(int rot,int lt,int rt,int lq,int rq)
{
if(lt>=lq&&rt<=rq)return a[rot];
pushdown(rot);
int mid=(lt+rt)>>1;
if(rq<=mid)return query(rot<<1,lt,mid,lq,rq);
if(lq>mid)return query(rot<<1|1,mid+1,rt,lq,rq);
return query(rot<<1,lt,mid,lq,mid)+query(rot<<1|1,mid+1,rt,mid+1,rq);
}
int getin()
{
int x=0,f=1;char ch=getchar();
while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
if(ch=='-')f=-1,ch=getchar();
while(ch>='0'&&ch<='9')x=x*10+ch-48,ch=getchar();
return x*f;
}
int main()
{
n=getin(),m=getin();
for(int i=1;i<=n;i++)w[i]=getin();
build(1,1,n);
for(int i=1,opt,l,r,x;i<=m;i++)
{
opt=getin(),l=getin(),r=getin();
if(opt==1)x=getin(),update(1,1,n,l,r,x);
else printf("%lld\n",max(0ll,query(1,1,n,l,r).ans.b));
}
}