题面
题意
给出一串数,每次询问给出l,r,求其中满足l<=i<=j<=r时,a[i]+a[i+1]……a[j]的最大值.
做法
用线段树来做,不难发现要想求出线段树的每个节点的最大子串和十分容易,难点就在于区间的合并.
可以对于每个线段树的上的点所维护的区间[l,r]记录3个值,其中令l<=i<=j<=r
1.sum
2.maxl:[l,i]的最大值
3.maxl.[i,r]的最大值
4.mx:[i,j]的最大值
这样就可以合并区间a,b了:
1.new.sum=a.sum+b.sum
2.new.maxl=max(a.maxl,a.sum+b.lmax)
3.new.maxr=max(b.maxr,b.sum+s.maxr)
4.new.mx=max(a.mx,b.mx,a.maxr+b.maxl)
每次询问找到组成这段区间的点后合并即可
代码
#include<iostream>
#include<cstdio>
#include<vector>
#define ll long long
#define INF 0x3f3f3f3f3f3f3f3f
#define mid ((l+r)>>1)
#define N 50010
using namespace std;
ll n,m,num[N],tt=1;
struct Node
{
ll ls,rs,sum,lmax,rmax,mx;
}node[N<<1];
vector<ll>use;
inline Node hb(Node &res,Node u,Node v)
{
res.sum=u.sum+v.sum;
res.lmax=max(u.lmax,u.sum+v.lmax);
res.rmax=max(v.rmax,v.sum+u.rmax);
res.mx=max(max(u.mx,v.mx),u.rmax+v.lmax);
return res;
}
void build(ll now,ll l,ll r)
{
if(l==r)
{
node[now].sum=node[now].lmax=node[now].rmax=node[now].mx=num[l];
return;
}
if(l<=mid)
{
node[now].ls=++tt;
build(tt,l,mid);
}
if(mid<r)
{
node[now].rs=++tt;
build(tt,mid+1,r);
}
hb(node[now],node[node[now].ls],node[node[now].rs]);
}
void ask(ll now,ll l,ll r,ll u,ll v)
{
if(l==u&&v==r)
{
use.push_back(now);
return;
}
if(u<=mid) ask(node[now].ls,l,mid,u,min(mid,v));
if(v>mid) ask(node[now].rs,mid+1,r,max(u,mid+1),v);
}
inline ll work(ll u,ll v)
{
ll i,j;
Node tmp;
use.clear();
ask(1,1,n,u,v);
tmp=node[use[0]];
for(i=1;i<use.size();i++)
{
hb(tmp,tmp,node[use[i]]);
}
return tmp.mx;
}
int main()
{
ll i,j,p,q;
cin>>n;
for(i=1;i<=n;i++)
{
scanf("%lld",&num[i]);
}
build(1,1,n);
cin>>m;
for(i=1;i<=m;i++)
{
scanf("%lld%lld",&p,&q);
printf("%lld\n",work(p,q));
}
}