传送门
解析:
对于这种问题,如果不考虑修改的话可能会有一些做法通过预处理来降低询问复杂度
而其中一种通过增加预处理复杂度来降低询问复杂度的做法我们称之为猫树(出自猫锟的博客)
对于不带修改的区间最大子段和问题,利用猫树可以做到 O ( n log n ) O(n \log n) O(nlogn)的预处理和 O ( 1 ) O(1) O(1)回答每个询问。
猫树其实是一个类似线段树的结构,其实这道题上猫树的应用就有点像离线分治了。
不过猫树可以做到强制在线。
于是就可以把询问开到
1
e
7
1e7
1e7出毒瘤题了
设当前询问区间为
l
,
r
l,r
l,r。
首先假设我们找到了一个节点,
l
,
r
l,r
l,r是它的子区间,且
l
,
r
l,r
l,r分别在它不同的子节点中。
可以证明,这样的节点是唯一的。假设这一个节点的中点为 m i d mid mid。
利用DP求最大子段和的方式,我们可以 O ( n ) O(n) O(n)处理出 [ l , m i d ] [l,mid] [l,mid]的最大子段和和最大后缀和, [ m i d + 1 , r ] [mid+1,r] [mid+1,r]的最大子段和和最大前缀和。显然答案要么在 [ l , m i d ] [l,mid] [l,mid]要么在 [ m i d + 1 , r ] [mid+1,r] [mid+1,r]要么就是横跨,三个拿来比较一下就行了。
所以问题是满足条件的区间怎么找。
显然是 l , r l,r l,r对应叶子节点在猫树上的LCA,强行将猫树大小转为 2 k 2^k 2k,预处理 l o g log log数组就可以 O ( 1 ) O(1) O(1)找到LCA了。
代码:
#include<bits/stdc++.h>
#define ll long long
#define gc get_char
#define re register
#define cs const
namespace IO{
inline char get_char(){
static cs int Rlen=1<<20|1;
static char buf[Rlen],*p1,*p2;
return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin),p1==p2)?EOF:*p1++;
}
inline int getint(){
re char c;
re bool f=0;
while(!isdigit(c=gc()))f^=c=='-';re int num=c^48;
while(isdigit(c=gc()))num=(num+(num<<2)<<1)+(c^48);
return f?-num:num;
}
}
using namespace IO;
inline int max(int a,int b){return a>b?a:b;}
cs int N=1<<19|7;
int n,m,a[N],logn[N],pos[N];
int p[21][N],s[21][N];
inline void build(int k,int l,int r,int dep){
if(l==r){
pos[l]=k;
return ;
}
int mid=(l+r)>>1;
build(k<<1,l,mid,dep+1);
build(k<<1|1,mid+1,r,dep+1);
int pre,sum;
p[dep][mid]=s[dep][mid]=pre=sum=a[mid];
if(sum<0)sum=0;
for(int re i=mid-1;i>=l;--i){
pre+=a[i],sum+=a[i];
s[dep][i]=max(s[dep][i+1],pre);
p[dep][i]=max(p[dep][i+1],sum);
if(sum<0)sum=0;
}
p[dep][mid+1]=s[dep][mid+1]=pre=sum=a[mid+1];
if(sum<0)sum=0;
for(int re i=mid+2;i<=r;++i){
pre+=a[i],sum+=a[i];
s[dep][i]=max(s[dep][i-1],pre);
p[dep][i]=max(p[dep][i-1],sum);
if(sum<0)sum=0;
}
}
inline int query(int l,int r){
if(l==r)return a[l];
int dep=logn[pos[l]]-logn[pos[l]^pos[r]];
return max(max(p[dep][l],p[dep][r]),s[dep][l]+s[dep][r]);
}
signed main(){
n=getint();
for(int re i=1;i<=n;++i)a[i]=getint();
int len=1;
while(len<n)len<<=1;
build(1,1,len,1);
for(int re i=2;i<=(len<<1);++i)logn[i]=logn[i>>1]+1;
m=getint();
while(m--){
int l=getint(),r=getint();
std::cout<<query(l,r)<<"\n";
}
return 0;
}