Ynoi2018 末日时在做什么?有没有空?可以来拯救吗?

Link
这个是\(O(n\sqrt{n\log n})\)的过不去的辣鸡做法。
还是分块。每块还是维护一下\(sum,pre,suf,ans\)的值/凸包。
修改对于整块的打个标记就完事了。
零散的考虑暴力修改,\(a,sum,suf,pre\)可以直接暴力。
然后用类似于Subtask 3的合并方式一样分治处理\(ans\)
这样我们就得到了一个\(O(n\sqrt n\log n)\)的过不去做法。
考虑到区间加只有正数,所以每个凸包上的最优决策点单调,这样打一次标记的复杂度变成了均摊\(O(1)\),通过调整块大小可以让复杂度降至\(O(n\sqrt{n\log n})\)
然后还有几个卡常的小优化:
\(1.\)每个块记录一下\(pre,suf,ans\)的最优决策点是否已经到了最右边,如果到了最右边的话那么重构就只需要扫一遍区间求个区间和,打标记的时候直接加上\(len*tag\)
\(2.\)由于加的是正数,所以最大子段和的长度单增。
因此重构时记录重构前\(ans\)最优决策点的\(x\),分治的时候如果当前区间长度\(<x\)就直接结束。
\(3.\)区间长度\(\le s\)的话暴力求\(ans\),同时把块长设为\(8s-\epsilon\)保证递归树不超过\(3\)层。

#include<cstdio>
#include<cctype>
#include<numeric>
#include<cstring>
#include<algorithm>
using i64=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(i64 x){int top=0;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=100007,M=120;const i64 inf=1ll<<50;
int n,m,bel[N],id[N];i64 tmp[M];
struct dot{i64 x,y;}stk[M];
dot operator+(const dot&a,const dot&b){return {a.x+b.x,a.y+b.y};}
int cmp(const dot&a,const dot&b,const dot&c){return (a.y-c.y)*(a.x-b.x)>=(a.y-b.y)*(a.x-c.x);}
int cmp(const dot&a,const dot&b,const i64&k){return k*(b.x-a.x)>=b.y-a.y;}
int cmp(const dot&a,const dot&b,const dot&c,const dot&d){return (c.y-d.y)*(a.x-b.x)>=(a.y-b.y)*(c.x-d.x);}
void convex(dot*stk,int top){int i=2,n=top;for(top=1;i<=n;stk[++top]=stk[i++]) while(top>1&&cmp(stk[top-1],stk[top],stk[i]))--top;}
void ins(const dot&a){stk[a.x].y=std::max(stk[a.x].y,a.y);}
struct data{i64 sum,pre,suf,ans;};
data operator+(const data&a,const data&b){return {a.sum+b.sum,std::max(a.pre,a.sum+b.pre),std::max(b.suf,b.sum+a.suf),std::max(std::max(a.ans,b.ans),a.suf+b.pre)};}
struct block
{
#define mid ((l+r)>>1)
    int n,p1,p2,p3,c1,c2,c3,f,mn;i64 tag,a[M];
    dot pre[M],suf[M],ans[M];data val;
    int bf(int l,int r)
    {
	i64 s=0;
	for(int i=l+1;i<=r;++i) s+=a[i],ans[i]={i-l,s};
        for(int i=l+2;i<=r;++i){s=0;dot*p=ans+l+1;for(int j=i;j<=r;++j,++p)s+=a[j],p->y=std::max(p->y,s);}
	return convex(ans+l,c3=r-l),c3;
    }
    int solve(int l,int r)
    {
	if(r-l<mn) return 0;
	if(r-l<=14) return bf(l,r);
	int l1=solve(l,mid),l2=solve(mid,r),i=1,j=1;i64 s1=0,s2=0;
	for(int i=1;i<=r-l;++i)    stk[i]={i,-inf};
	for(int i=l+1;i<=l+l1;++i) ins(ans[i]);
	for(int i=mid+1;i<=mid+l2;++i) ins(ans[i]);
	s1=s2=c1=c2=0;
	for(int i=mid+1;i<=r;++i) s1+=a[i],++c1,pre[c1]={c1,s1};
        for(int i=mid;i>=l+1;--i) s2+=a[i],++c2,suf[c2]={c2,s2};
	convex(pre,c1),convex(suf,c2),ins(pre[i]+suf[j]);
        while(i^c1&&j^c2) ++(cmp(pre[i],pre[i+1],suf[j],suf[j+1])?j:i),ins(pre[i]+suf[j]);
	while(i^c1) ++i,ins(pre[i]+suf[j]);
	while(j^c2) ++j,ins(pre[i]+suf[j]);
	return convex(stk,c3=r-l),memcpy(ans+l+1,stk+1,c3<<4),c3;
    }
    void jump(dot*a,int&p,const int&n){for(;p^n;++p)if(cmp(a[p],a[p+1],-tag))break;}
    void rebuild()
    {
	i64 s1=0,s2=0;
        for(int i=1;i<=n;++i) a[i]+=tag;
	tag=0;
	if(!f){f=1;for(int i=1;i<=n;++i)f&=(a[i]>=0);}
	if(f){val.sum=0;for(int i=1;i<=n;++i)val.pre=val.suf=val.ans=(val.sum+=a[i]);return ;}
        mn=ans[p3].x,solve(0,n),s1=s2=c1=c2=0;
	for(int i=1;i<=n;++i) s1+=a[i],++c1,pre[c1]={c1,s1};
	for(int i=n;i>=1;--i) s2+=a[i],++c2,suf[c2]={c2,s2};
	convex(pre,c1),convex(suf,c2),jump(pre,p1=1,c1),jump(suf,p2=1,c2),jump(ans,p3=1,c3);
	f=p1==c1&&p2==c2&&p3==c3,val=data{s1,std::max(pre[p1].y,0ll),std::max(suf[p2].y,0ll),std::max(ans[p3].y,0ll)};
    }
    i64 abs(const dot&a){return std::max(a.x*tag+a.y,0ll);}
    void add(i64 x)
    {
        if(f) return (void)(tag+=x,val.pre=val.suf=val.ans=(val.sum+=n*x));
        tag+=x,jump(pre,p1,c1),jump(suf,p2,c2),jump(ans,p3,c3);
	f=p1==c1&&p2==c2&&p3==c3,val={val.sum+n*x,abs(pre[p1]),abs(suf[p2]),abs(ans[p3])};
    }
    data query(int l,int r)
    {
        data ans={0,0,0,0};int m=0;i64 mn=0;
        for(int i=l;i<=r;++i) tmp[++m]=a[i]+tag;
        for(int i=1;i<=m;++i) ans.pre=std::max(ans.pre,ans.sum+=tmp[i]);
	ans.sum=0;
        for(int i=m;i>=1;--i) ans.suf=std::max(ans.suf,ans.sum+=tmp[i]);
	std::partial_sum(tmp+1,tmp+m+1,tmp+1);
        for(int i=1;i<=m;++i) ans.ans=std::max(ans.ans,tmp[i]-mn),mn=std::min(mn,tmp[i]);
        return ans;
    }
#undef mid
}b[911];
int main()
{
    n=read(),m=read();int B=110;
    for(int i=0,c=1;i<n;b[c].n=std::min(n-i,B),b[c++].rebuild(),i+=B) for(int j=1;j<=B&&i+j<=n;++j) b[c].a[j]=read(),bel[i+j]=c,id[i+j]=j;
    while(m--)
    {
        if(read()==1)
        {
	    int l=read(),r=read();i64 x=read();
            if(bel[l]==bel[r]) {for(int i=id[l];i<=id[r];++i)b[bel[l]].a[i]+=x;b[bel[l]].rebuild();}
            else
            {
                for(int i=id[l];i<=B;++i) b[bel[l]].a[i]+=x;
                for(int i=bel[l]+1;i<bel[r];++i) b[i].add(x);
                for(int i=1;i<=id[r];++i) b[bel[r]].a[i]+=x;
		b[bel[l]].rebuild(),b[bel[r]].rebuild();
            }
        }
        else
        {
	    int l=read(),r=read();
            if(bel[l]==bel[r]) write(b[bel[l]].query(id[l],id[r]).ans);
            else
            {
                data ans=b[bel[l]].query(id[l],B);
                for(int i=bel[l]+1;i<bel[r];++i) ans=ans+b[i].val;
		ans=ans+b[bel[r]].query(1,id[r]),write(ans.ans);
            }
        }
    }
    return IO::Flush(),0;
}
posted @ 2020-03-03 20:58  Shiina_Mashiro  阅读(314)  评论(2编辑  收藏  举报