Ynoi2015 世上最幸福的女孩
Link
先考虑单点修改的做法:
线段树上每个节点维护四个信息:\(sum,pre,suf,ans\)。分别代表区间和,区间最大前缀和,区间最大后缀和,区间最大子段和。
合并:
\(sum_x=sum_l+sum_r\)
\(pre_x=\max(pre_l,sum_l+pre_r)\)
\(suf_x=\max(suf_r,sum_r+suf_l)\)
\(ans_x=\max(\max(ans_l,ans_r),suf_l+pre_r)\)
考虑如何将修改扩展到全局修改。
假如全局已经被加上了\(tag\),现在希望求某个点的新的\(sum,pre,suf,ans\)。
\(sum\)非常简单就不讲了。
\(pre\)和\(suf\)具有对称性,只考虑\(pre\)。
如果将\((x,pre_x)\)看做一个点(\(pre_x\)表示长度为\(x\)的前缀和),那么我们就是要求\(tag*x+y\)的最大值。
维护一个凸包,查询的时候二分一下。
pushup就是将\(pre_{rs}\)平移\((len_{ls},sum_{ls})\)之后和\(pre_{rs}\)接起来。
现在看\(ans\),在单点修改的部分\(ans\)是与左右儿子的\(ans,suf,pre\)挂钩的,所以我们类似地考虑。
将\((x,ans_x)\)看做一个点(\(ans_x\)表示长度为\(x\)的最大子段和),那么我们就是要求\(tag*x+y\)的最大值。
还是维护一个凸包,查询的时候二分一下。
pushup稍微麻烦一点,首先我们合并\(ans_{ls},ans_{rs}\),然后求出\(suf_{ls},pre_{rs}\)的Mincowsky和,再将这两个凸包合并。
这样我们可以在一开始建出一棵每个节点维护了\(pre,suf,ans\)三个凸包的线段树,每次询问在\(\log\)个节点上二分出最优的\(pre,suf,ans\)然后再按Subtask 2的做法合并。这样子的时间复杂度是\(O(n\log n+m\log^2n)\)的。
我们可以将所有询问离线之后按\(tag\)排序,这样凸包上指针的移动就是单调的,因此时间复杂度是\(O((n+m)\log n)\)。
#include<cstdio>
#include<cctype>
#include<algorithm>
using std::sort;
using ll=long long;
namespace IO
{
char ibuf[(1<<21)+1],obuf[(1<<21)+1],st[21],*iS,*iT,*oS=obuf,*oT=obuf+(1<<21);
char Get(){return (iS==iT? (iT=(iS=ibuf)+fread(ibuf,1,(1<<21)+1,stdin),(iS==iT? EOF:*iS++)):*iS++);}
void Flush(){fwrite(obuf,1,oS-obuf,stdout),oS=obuf;}
void Put(char x){*oS++=x;if(oS==oT)Flush();}
int read(){int x=0,c=Get(),f=1;while(!isdigit(c)&&c^'-')c=Get();if(c=='-')f=-1,c=Get();while(isdigit(c))x=x*10+c-48,c=Get();return f*x;}
void write(ll x){int top=0;if(x<0)Put('-'),x=-x;if(!x)Put('0');while(x)st[++top]=(x%10)+48,x/=10;while(top)Put(st[top--]);Put('\n');}
}using IO::read;using IO::write;
const int N=300007,M=600007;const ll inf=1ll<<50;
ll max(const ll&a,const ll&b){return a>b? a:b;}
int n,m,cnt;ll tag,a[N],ans[M];
struct dot{ll x,y;};
dot operator+(const dot&a,const dot&b){return {a.x+b.x,a.y+b.y};}
dot operator-(const dot&a,const dot&b){return {a.x-b.x,a.y-b.y};}
int operator<(const dot&a,const dot&b){return a.y*b.x<=a.x*b.y;}
struct convex
{
dot *stk;int top,now;
dot&operator[](const int&x){return stk[x];}
void upd(const dot&a){stk[a.x].y=max(stk[a.x].y,a.y);}
void ins(const dot&a){stk[++top]=a;}
void clr(const int&n){for(int i=1;i<=n;++i)stk[i]={i,-inf};top=n;}
void build()
{
if(top<=2) return;
int i=3,n=top;top=2,now=1;
for(;i<=n;++i)
{
if(stk[i].y==-inf) continue;
while(top>1&&(stk[top]-stk[top-1])<(stk[i]-stk[top-1])) --top;
stk[++top]=stk[i];
}
}
ll cal(){while(now^top&&(-tag)*(stk[now+1].x-stk[now].x)<(stk[now+1].y-stk[now].y))++now;return tag*stk[now].x+stk[now].y;}
};
struct data{ll sum,pre,suf,ans;};
data operator+(const data&a,const data&b){return {a.sum+b.sum,max(a.pre,a.sum+b.pre),max(a.suf+b.sum,b.suf),max(max(a.ans,b.ans),a.suf+b.pre)};}
namespace segtree
{
#define ls p<<1
#define rs p<<1|1
#define mid ((l+r)>>1)
dot prep[20][M],sufp[20][M],ansp[20][M],*ppre[20],*psuf[20],*pans[20];
convex pre[N<<2],suf[N<<2],ans[N<<2];ll sum[N<<2];
void init(){for(int i=0;i<20;++i)ppre[i]=prep[i],psuf[i]=sufp[i],pans[i]=ansp[i];}
void merge(convex&c,convex&a,convex&b,const dot&p)
{
for(int i=1;i<=a.top;++i) c.ins(a[i]);
for(int i=1;i<=b.top;++i) c.ins(p+b[i]);
c.build();
}
void merge(convex&c,convex&a,convex&b)
{
int i=1,j=1;
c.upd(a[i]+b[j]);
while(i^a.top&&j^b.top) ((a[i+1]-a[i])<(b[j+1]-b[j])? j:i)++,c.upd(a[i]+b[j]);
while(i^a.top) ++i,c.upd(a[i]+b[j]);
while(j^b.top) ++j,c.upd(a[i]+b[j]);
}
void build(int p,int l,int r,int d)
{
pre[p].stk=ppre[d],suf[p].stk=psuf[d],ans[p].stk=pans[d];
if(r==l+1) pre[p][2]=suf[p][2]=ans[p][2]={1,sum[p]=a[r]},pre[p][1]=suf[p][1]=ans[p][1]={0,0},pre[p].top=suf[p].top=ans[p].top=2;
else
{
build(ls,l,mid,d+1),build(rs,mid,r,d+1);
sum[p]=sum[ls]+sum[rs],merge(pre[p],pre[ls],pre[rs],{mid-l,sum[ls]}),merge(suf[p],suf[rs],suf[ls],{r-mid,sum[rs]}),++ans[p].stk,ans[p].clr(r-l);
for(int i=1;i<=ans[ls].top;++i) ans[p].upd(ans[ls][i]);
for(int i=1;i<=ans[rs].top;++i) ans[p].upd(ans[rs][i]);
merge(ans[p],suf[ls],pre[rs]),--ans[p].stk,ans[p][1]={0,0},++ans[p].top,ans[p].build();
}
pre[p].now=suf[p].now=ans[p].now=1,ppre[d]=pre[p].stk+pre[p].top,psuf[d]=suf[p].stk+suf[p].top,pans[d]=ans[p].stk+ans[p].top;
}
data query(int p,int l,int r,int L,int R)
{
if(L==l&&R==r) return {sum[p]+(r-l)*tag,pre[p].cal(),suf[p].cal(),ans[p].cal()};
if(R<=mid) return query(ls,l,mid,L,R);
if(L>=mid) return query(rs,mid,r,L,R);
return query(ls,l,mid,L,mid)+query(rs,mid,r,mid,R);
}
#undef ls
#undef rs
#undef mid
}
struct query{int l,r,id;ll tag;}q[M];
int operator<(const query&a,const query&b){return a.tag<b.tag;}
int main()
{
n=read(),m=read();
for(int i=1;i<=n;++i) a[i]=read();
for(int i=1,l,r;i<=m;++i) (read()==1)? tag+=read():(l=read(),r=read(),++cnt,q[cnt]={l,r,cnt,tag},0ll);
sort(q+1,q+cnt+1),tag=q[1].tag;
for(int i=1;i<=cnt;++i) q[i].tag-=tag;
for(int i=1;i<=n;++i) a[i]+=tag;
segtree::init(),segtree::build(1,0,n,0);
for(int i=1;i<=cnt;++i) tag=q[i].tag,ans[q[i].id]=segtree::query(1,0,n,q[i].l-1,q[i].r).ans;
for(int i=1;i<=cnt;++i) write(ans[i]);
return IO::Flush(),0;
}