Ynoi2013 D2T2
Link
看到最大子段和就要想到维护\(sum,pre,suf,ans\)四个信息。
考虑将序列分为\(\sqrt n\)块,对于每一块内,总共只有\(O(n)\)种不同的值域区间。
如果我们在每一块内,对每一种值域区间处理出整块的信息,那么就可以在\(O(n\sqrt n)\)的时间复杂度内求出素有询问的答案。
考虑对序列分治,分治底层的处理是trivial的,因此只需考虑如何合并\([l,mid],(mid,r]\)的信息。
首先可以通过归并排序得到当前序列区间按权值升序排序后的数组。
然后假如我们要求当前序列区间值域区间为\([L,R)\)答案,那么我们可以找到\([L,R]\)在\([l,mid]\)及\((mid,r]\)序列区间中对应的等价的值域区间,然后直接合并得到答案。
利用单调指针预处理\(\forall i\in[l,r],\min\{a_j|a_j\ge a_i\wedge j\in[l,mid]\},\min\{a_j|a_j\ge a_i\wedge j\in(mid,r]\}\)即可。
离线所有询问之后即做到复杂度\(O(n\sqrt n)+O(n)\)。
#include<cctype>
#include<cstdio>
#include<cstring>
#include<algorithm>
using i64=long long;
using std::max;
const int N=100007,B=260;
char ibuf[1<<23|1],*iS=ibuf;int n,m,L,R,it1,it2,a[N],h[N],r[N],rank[N],s[N];
int read(){int x=0,f=1;while(isspace(*iS))++iS;if(*iS=='-')f=-1,++iS;while(isdigit(*iS))(x*=10)+=*iS++&15;return f*x;}
struct data
{
i64 sum,pre,suf,ans;
data(){sum=pre=suf=ans=0;}
data(i64 val){sum=val,pre=suf=ans=max(val,0ll);}
data(i64 a,i64 b,i64 c,i64 d):sum(a),pre(b),suf(c),ans(d){}
}t[2*N];
data operator+(const data&l,const data&r){return data(l.sum+r.sum,max(l.pre,l.sum+r.pre),max(r.suf,r.sum+l.suf),max(max(l.ans,r.ans),l.suf+r.pre));}
struct node
{
data*a;int*val,n;
node(){}
node(int N):a(t+it1),val(s+it2),n(N+1){it1+=n*n,it2+=n;}
data&get(int x,int y){return a[x*n+y];}
void init(int x){get(0,1)=data(x),get(0,0)=get(1,1)=data(),val[0]=x,val[1]=2e9;}
void merge(node&ls,node&rs)
{
int*l=s+it2,*r=l+n;
it2+=n+n,std::merge(ls.val,ls.val+ls.n-1,rs.val,rs.val+rs.n,val);
for(int i=0,itl=0,itr=0;i<n;l[i]=itl,r[i]=itr,++i)
{
for(;ls.val[itl]<val[i];++itl);
for(;rs.val[itr]<val[i];++itr);
}
for(int i=0;i<n;++i) for(int j=i;j<n;++j) get(i,j)=ls.get(l[i],l[j])+rs.get(r[i],r[j]);
}
void getrank(){int p=1;for(int i=0;i<n;++i)for(;p<=::n&&h[p]<=val[i];rank[p++]=i);rank[p]=n-1;}
}now;
struct query
{
int ql,qr,vl,vr;i64 suf,ans;
void merge(const data&x){ans=max(max(ans,x.ans),suf+x.pre),suf=max(suf+x.sum,x.suf);}
void update()
{
if(qr<L||R<ql) return ;
if(ql<=L&&R<=qr) merge(now.get(rank[vl],rank[vr]));
else for(int i=max(ql,L);i<=std::min(qr,R);++i) if(vl<=r[i]&&r[i]<vr) ans=max(ans,suf+=a[i]),suf=max(suf,0ll);
}
}q[N];
node build(int l,int r)
{
node ans(r-l+1);int mid=(l+r)/2;
if(l==r) return ans.init(a[l]),ans;
node ls=build(l,mid),rs=build(mid+1,r);
return ans.merge(ls,rs),ans;
}
void solve()
{
it1=it2=0,now=build(L,R),now.getrank();
for(int i=1;i<=m;++i) q[i].update();
}
int main()
{
fread(ibuf,1,1<<23,stdin),n=read(),m=read();
for(int i=1;i<=n;++i) h[i]=a[i]=read();
std::sort(h+1,h+n+1);
for(int i=1;i<=n;++i) r[i]=std::lower_bound(h+1,h+n+1,a[i])-h;
for(int i=1;i<=m;++i) q[i].ql=read(),q[i].qr=read(),q[i].vl=std::lower_bound(h+1,h+n+1,read())-h,q[i].vr=std::upper_bound(h+1,h+n+1,read())-h;
for(L=1;L<=n;L+=256) R=std::min(L+255,n),solve();
for(int i=1;i<=m;++i) printf("%lld\n",q[i].ans);
}