假设所有操作都是对整个序列的。考虑每个子区间,区间和与其被加的值构成一次函数关系。最大子段和相当于多个子区间取最大值,答案显然就在这些一次函数构成的下凸壳上。如果预处理出凸壳,只要在凸壳上暴力跳就可以回答询问了,因为加的都是正数,并且斜率不同的一次函数数量是O(n)的。暴力建凸壳的复杂度是O(n2)的。

  那么考虑分块。每个块预处理出凸壳。区间加时,对于整块在凸壳上暴跳,零散部分暴力重构。查询时合并区间,类似于单点加的线段树做法,维护块内最大前缀和、最大后缀和。块大小取n1/3时最优。

  造凸壳写挂调了1h,退役了。

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;
#define ll long long
#define N 50010
#define NUM 2000
#define BLOCK 100
#define inf 10000000000000000ll
char getc(){char c=getchar();while ((c<'A'||c>'Z')&&(c<'a'||c>'z')&&(c<''||c>'')) c=getchar();return c;}
int gcd(int n,int m){return m==?n:gcd(m,n%m);}
int read()
{
int x=,f=;char c=getchar();
while (c<''||c>'') {if (c=='-') f=-;c=getchar();}
while (c>=''&&c<='') x=(x<<)+(x<<)+(c^),c=getchar();
return x*f;
}
int n,m,pos[N],num,block;
ll a[N],tmp[N],PRE[N],SUF[N];
struct line
{
ll k,b;
ll f(ll x){return k*x+b;}
};
ll cross(line x,line y){return (x.b-y.b-)/(y.k-x.k)+;}
struct hull
{
line a[BLOCK];int cnt,cur;
void ins(line x){while (cnt&&x.b>=a[cnt].b||cnt>&&x.f(cross(a[cnt],a[cnt-]))>a[cnt].f(cross(a[cnt],a[cnt-]))) cnt--;a[++cnt]=x;}
void clear(){cnt=,cur=,ins((line){,});}
void jump(ll x){while (cur<cnt&&a[cur].f(x)<a[cur+].f(x)) cur++;}
ll get(ll x){return a[cur].f(x);}
};
struct data
{
hull pre,suf,seq;int L,R;ll lazy,sum;
void build()
{
for (int i=L;i<=R;i++) a[i]+=lazy;lazy=;
sum=;for (int i=L;i<=R;i++) sum+=a[i];
ll x=;pre.clear();
for (int i=L;i<=R;i++) pre.ins((line){i-L+,x+=a[i]});
x=;suf.clear();
for (int i=R;i>=L;i--) suf.ins((line){R-i+,x+=a[i]});
seq.clear();
for (int k=;k<=R-L+;k++)
{
ll s=-inf;x=;for (int i=L;i<=L+k-;i++) x+=a[i];
for (int i=L+k-;i<=R;i++) s=max(s,x),x+=a[i+]-a[i-k+];
seq.ins((line){k,s});
}
}
void add(int x){sum+=1ll*(R-L+)*x,lazy+=x,pre.jump(lazy),suf.jump(lazy),seq.jump(lazy);}
ll maxpre(){return pre.get(lazy);}
ll maxsuf(){return suf.get(lazy);}
ll maxseq(){return seq.get(lazy);}
}f[NUM];
ll getseq(int l,int r)
{
ll s=,ans=;
for (int i=l;i<=r;i++)
{
s+=tmp[i];
if (s<) s=;
ans=max(ans,s);
}
return ans;
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("bzoj5089.in","r",stdin);
freopen("bzoj5089.out","w",stdout);
const char LL[]="%I64d\n";
#else
const char LL[]="%lld\n";
#endif
n=read(),m=read();block=*pow(n,1.0/)+;num=(n-)/block+;
for (int i=;i<=n;i++) a[i]=read();
for (int i=;i<=num;i++)
{
f[i].L=f[i-].R+,f[i].R=min(n,f[i].L+block-);
for (int j=f[i].L;j<=f[i].R;j++) pos[j]=i;
f[i].build();
}
while (m--)
{
char c=getc();
if (c=='A')
{
int l=read(),r=read(),x=read();
if (pos[l]==pos[r])
{
for (int i=l;i<=r;i++) a[i]+=x;
f[pos[l]].build();
}
else
{
for (int i=pos[l]+;i<pos[r];i++) f[i].add(x);
for (int i=l;i<=f[pos[l]].R;i++) a[i]+=x;f[pos[l]].build();
for (int i=f[pos[r]].L;i<=r;i++) a[i]+=x;f[pos[r]].build();
}
}
else
{
int l=read(),r=read();
if (pos[l]==pos[r])
{
for (int i=l;i<=r;i++) tmp[i]=a[i]+f[pos[l]].lazy;
printf(LL,getseq(l,r));
}
else
{
for (int i=l;i<=f[pos[l]].R;i++) tmp[i]=a[i]+f[pos[l]].lazy;
for (int i=f[pos[r]].L;i<=r;i++) tmp[i]=a[i]+f[pos[r]].lazy;
ll ans=max(getseq(l,f[pos[l]].R),getseq(f[pos[r]].L,r)),s;
for (int i=pos[l]+;i<pos[r];i++) ans=max(ans,f[i].maxseq());
for (int i=pos[l];i<=pos[r];i++) PRE[i]=SUF[i]=;
s=;for (int i=f[pos[l]].R;i>=l;i--) s+=tmp[i],SUF[pos[l]]=max(SUF[pos[l]],s);
s=;for (int i=f[pos[r]].L;i<=r;i++) s+=tmp[i],PRE[pos[r]]=max(PRE[pos[r]],s);
for (int i=pos[l]+;i<pos[r];i++) SUF[i]=max(f[i].maxsuf(),SUF[i-]+f[i].sum);
for (int i=pos[r]-;i>pos[l];i--) PRE[i]=max(f[i].maxpre(),PRE[i+]+f[i].sum);
for (int i=pos[l];i<pos[r];i++) ans=max(ans,SUF[i]+PRE[i+]);
printf(LL,ans);
}
}
}
return ;
}

BZOJ5089 最大连续子段和(分块)的更多相关文章

  1. bzoj5089 最大连续子段和 分块+复杂度分析+凸包

    题目传送门 https://lydsy.com/JudgeOnline/problem.php?id=5089 题解 本来打算迟一点再写这个题解的,还有一个小问题没有弄清楚. 不过先写一下存个档吧. ...

  2. 【bzoj5089】最大连续子段和 分块+单调栈维护凸包

    题目描述 给出一个长度为 n 的序列,要求支持如下两种操作: A  l  r  x :将 [l,r] 区间内的所有数加上 x : Q  l  r : 询问 [l,r] 区间的最大连续子段和. 其中,一 ...

  3. BZOJ5089: 最大连续子段和

    维护一个序列支持以下操作:区间加,区间求最大子段和.n<=50000,m<=50000. 我TM再也不写分块了... 先分块,对于块整体加的操作,假设块里面有若干二元组(x,y),表示一个 ...

  4. HDOJ-1003 Max Sum(最大连续子段 动态规划)

    http://acm.hdu.edu.cn/showproblem.php?pid=1003 给出一个包含n个数字的序列{a1,a2,..,ai,..,an},-1000<=ai<=100 ...

  5. HDU 1003:Max Sum(DP,连续子段和)

    Max Sum Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total Su ...

  6. HPU 1007: 严格递增连续子段(贪心)

    1007: 严格递增连续子段 [模拟] 时间限制: 1 Sec 内存限制: 128 MB提交: 244 解决: 18 统计 题目描述 给定一个有NN个正整数组成的序列,你最多可以改变其中一个元素,可以 ...

  7. HDU 1003 最大连续子段和

    题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=1003 Max Sum Time Limit: 2000/1000 MS (Java/Others)M ...

  8. [题解](线段树最大连续子段和)POJ_3667_Hotel

    题意:1.求一个最靠左的长x的区间全部为0,并修改为1,输出这个区间的左端点 2.修改一个区间为0 实际上是维护最大连续子段和,原来也写过 大概需要维护一个左/右最大子段和,当前这段最大子段长,再维护 ...

  9. JDOJ 2982: 最大连续子段和问题

    洛谷 P1115 最大子段和 洛谷传送门 JDOJ 2982: 最大连续子段和问题 JDOJ传送门 题目描述 给出一段序列,选出其中连续且非空的一段使得这段和最大. 输入格式 第一行是一个正整数NN, ...

随机推荐

  1. CAN总线的显性电平为什么能覆盖隐性电平?

    摘要:在CAN总线中,显性电平是强驱动,隐性电平时弱驱动,因此当有的节点发送显性电平有的节点发送隐性电平时,总线上呈现的肯定是强驱动的状态,这就是CAN总线显性电平可以覆盖隐性电平的原因. 大家都知道 ...

  2. 报错:Cannot create PoolableConnectionFactory (The server time zone value 'CST' is unrecognized or represents more than one time zone. You must configure either the server or JDBC driver (via the serverT

    报错:Cannot create PoolableConnectionFactory (The server time zone value 'CST' is unrecognized or repr ...

  3. 开始试用Dynamics 365

    1. 访问地址: https://trials.dynamics.com/Dynamics365/Signup/ ,默认选择第一个应用,也可以选择其他的,不影响,之后还可以添加更多的应用程序. 2. ...

  4. Java中的Union Types和Intersection Types

    前言 Union Type和Intersection Type都是将多个类型结合起来的一个等价的"类型",它们并非是实际存在的类型. Union Type Union type(联 ...

  5. Algorithm - 贪心算法使用场景 ( LEETCODE —— Best Time to Buy and Sell Stock II)

    先看一道leetcode题: Best Time to Buy and Sell Stock II Say you have an array for which the ith element is ...

  6. Python set 集合

    简介 python的set和其他语言类似, 是一个无序不重复元素集, 基本功能包括关系测试和消除重复元素. 集合对象还支持union(联 合), intersection(交), difference ...

  7. 华策光通信: LED可见光通信室内定位项目获最具投资价值奖

    3月21日上午,一场持续3个多小时的O2O领域的创业DemoShow在深圳科兴科学园会议中心激烈上演.来自华策光通信的基于LED可见光通信室内精准定位项目作为LED与室内定位领域的跨界融合项目经过精彩 ...

  8. YQCB冲刺周第七天

    站立会议 任务看板 燃尽图 今天的任务为实现个人设置中的修改密码.设置金额的功能.以及界面的美化. 遇到的问题为修改自己密码时获得当前用户的id问题.

  9. web01-helloworld

    新建一个Java web项目,名字为web01 在项目中新建一个servlet,名字为SimpleHello 修改doGet()方法,为: public void doGet(HttpServletR ...

  10. ubuntu下screen的使用

    ubuntu下screen的使用 日常在通过SSH远程到服务器训练网络和深度学习的相关代码,经常需要花费很长的时间.利用SSH远程连接服务器,运行程序需要保证在此期间窗口不能关闭并且连接不能断开,否则 ...