zoukankan      html  css  js  c++  java
  • 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(nlog n+mlog^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('
    ');}
    }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;
    }
    
  • 相关阅读:
    2020.8.20收获
    2020.8.19
    2020.8.21收获
    2020.8.24收获
    UIScrollView滑动动作结束的侦听函数
    iphone 自定义UISwitch
    总结SQLite不支持的SQL语法有哪些
    去除nsstring中的空格
    ObjectiveC中判断字符串是否包含其他字符串
    设置IPHONE顶部的状态栏的样式
  • 原文地址:https://www.cnblogs.com/cjoierShiina-Mashiro/p/12404450.html
Copyright © 2011-2022 走看看