琢磨了很久的一题,经过历史考试的思考,终于懂了。
现在建立一个如下模型:
sum[1]=a[1]+a[2]+a[3]+a[4]+…+a[i]
sum[2]=a[2]+a[3]+a[4]+…+a[i]
sum[3]=a[3]+a[4]+…+a[i]
…
sum[i]=a[i] (假设a[1]-a[i]均不等)
然后,需要我们求[1,i]的最大子段和,这不就是求sum[1]-sum[i]的最大值吗?确实是这样的。 可能会问,题目不是说a[1]-a[i]有可能相等嘛,给我一个均不等的样例有什么用呢? 因为我们可以把一长段的有相同元素的数列分为若干段在段内无相同元素的数列。
为了快速进行累加,可以通过线段树来实现维护,对于以上的那个模型来说,就是add(1,1,n,1,i,a[i])。
对于整个数列来说,将每个位置的数字i,找到前一个与它相同的数字的位置,记录为pre[i],然后,我们就可以把[pre[i]+1,i]这个区间内所有的数都累加上a[i]了。
通过累加后我们会发现,对于某个叶子节点的T[k].sum来说,这个值就是:a[i]+a[i+1]+a[i+2]+…+a[n],除去里面相同的元素。那么它的最长子段和,不就是维护一个历史最大值吗?
所以这题,就成功转化为了:区间修改+查询区间历史最大值裸题了。
#include <bits/stdc++.h>
using namespace std;
const int N=1e5+5;
int n,m;
int a[N],pre[N],pos[N<<1],ans[N];
struct number{int x,y,id;}num[N];
struct node{int sum,max,tagsum,tagmax;}T[N<<2];
inline bool cmp(number a,number b)
{
return a.y<b.y;
}
inline void pushdown(int k)
{
T[k<<1].max=max(T[k<<1].max,T[k<<1].sum+T[k].tagmax);
T[k<<1].sum+=T[k].tagsum;
T[k<<1].tagmax=max(T[k<<1].tagmax,T[k<<1].tagsum+T[k].tagmax);
T[k<<1].tagsum+=T[k].tagsum;
T[k<<1|1].max=max(T[k<<1|1].max,T[k<<1|1].sum+T[k].tagmax);
T[k<<1|1].sum+=T[k].tagsum;
T[k<<1|1].tagmax=max(T[k<<1|1].tagmax,T[k<<1|1].tagsum+T[k].tagmax);
T[k<<1|1].tagsum+=T[k].tagsum;
T[k].tagsum=0; T[k].tagmax=0;
}
void change(int k,int l,int r,int qx,int qy,int v)
{
if (qx<=l && r<=qy)
{
T[k].sum+=v;
T[k].max=max(T[k].max,T[k].sum);
T[k].tagsum+=v;
T[k].tagmax=max(T[k].tagmax,T[k].tagsum);
return;
}
pushdown(k);
int mid=l+r>>1;
if (qx<=mid) change(k<<1,l,mid,qx,qy,v);
if (mid<qy) change(k<<1|1,mid+1,r,qx,qy,v);
T[k].sum=max(T[k<<1].sum,T[k<<1|1].sum);
T[k].max=max(T[k<<1].max,T[k<<1|1].max);
}
int query(int k,int l,int r,int qx,int qy)
{
if (qx<=l && r<=qy) return T[k].max;
pushdown(k);
int mid=l+r>>1;
int res=-2e9;
if (qx<=mid) res=max(res,query(k<<1,l,mid,qx,qy));
if (mid<qy) res=max(res,query(k<<1|1,mid+1,r,qx,qy));
return res;
}
int main(){
scanf("%d",&n);
for (register int i=1; i<=n; ++i)
{
scanf("%d",&a[i]);
pre[i]=pos[a[i]+(int)1e5];
pos[a[i]+(int)1e5]=i;
}
scanf("%d",&m);
for (register int i=1; i<=m; ++i) scanf("%d%d",&num[i].x,&num[i].y),num[i].id=i;
sort(num+1,num+m+1,cmp);
int j=1;
for (register int i=1; i<=n; ++i)
{
change(1,1,n,pre[i]+1,i,a[i]);
while (j<=m && num[j].y<=i) ans[num[j].id]=query(1,1,n,num[j].x,num[j].y),j++;
}
for (register int i=1; i<=m; ++i) printf("%d\n",ans[i]);
return 0;
}