(C) sequence
题意
给出长度为n的数组a和数组b,求下式(注意会出现负数的情况)
分析
单调队列+ZKW线段树
首先,min(al…ar),我们可以单调队列从前往后、从后往前用求。举个例子,当我们从前往后遍历的时候,维护一个单调递增的单调队列,对于每个下标i,都可以求出i左侧 距离i最近并且小于a[i]的下标j1。与此同理,我们从后往前遍历,同样维护一个单调递增的单调队列,对于每个下标i,都可以求出i右侧 距离i最近并且小于a[i]的下标j2。那么[j1+1,j2-1]这段区间的所有元素都大于等于a[i]。
那么对于sum(bl…br),我们该怎么求呢?
区间求和,可以联想到前缀和,两个值一减,即为区间和。我们可以维护两棵线段树,一颗维护最大前缀和,一颗维护最小前缀和。
我们所要求的答案是两者相乘的最大值。依次遍历a,对于每个位置i,求出区间[l,r](上述提到的区间)。
若a[i]为零,显然不管sum(l,r)为何值,结果都为0。
若a[i]为正,sum(l…r)也应该取得最大值,即[i,r]前缀和的最大值-[l,i-1]前缀和的最小值(不懂可以看看代码–)。
若a[i]为负,sum(l…r)也应该取得最小值,即[i,r]前缀和的最小值-[l,i-1]前缀和的最大值(不懂可以看看代码–)。
注意:ZKW线段树对于下标0处理情况比较特殊,这里我采用了分类讨论==、请谅解
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll M = 1<<22;
const int N = 3e6+5;
const ll INF = 1e18;
ll tree1[M+M+5],tree2[M+M+5];//两颗线段树
int n,a[N],b[N],l1[N],r1[N],qu[N];//l1,r1存取每个i的左右端点
ll query_max(int l,int r,ll tree[])
{
ll ans=0;
for(l+=M-1,r+=M+1;l^r^1;l>>=1,r>>=1)
{
if(~l&1)
ans=max(ans,tree[l^1]);
if(r&1)
ans=max(ans,tree[r^1]);
}
return ans;
}
ll query_min(int l,int r,ll tree[])
{
ll ans=INF;
for(l+=M-1,r+=M+1;l^r^1;l>>=1,r>>=1)
{
if(~l&1)
ans=min(ans,tree[l^1]);
if(r&1)
ans=min(ans,tree[r^1]);
}
return ans;
}
void solve(int ld[])
{
ll l=0,r=0;
for(int i=1;i<=n;i++)
{
while(r>l&&a[qu[r]]>=a[i])//单调递增
r--;
ld[i]=qu[r]+1;
qu[++r]=i;
}
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
for(int i=1;i<=n;i++)
scanf("%d",&b[i]);
for(int i=1;i<=n;i++)//前缀和
{
tree1[i+M]=tree1[i+M-1]+b[i];
tree2[i+M]=tree1[i+M];
}
tree1[M]=-INF;//赋值
tree2[M]=INF;
for(int i=M-1;i>=1;i--)//建树
{
tree1[i]=max(tree1[i+i],tree1[i+i+1]);
tree2[i]=min(tree2[i+i],tree2[i+i+1]);
}
solve(l1);//以下四行是用单调队列求左右端点
reverse(a+1,a+n+1);
solve(r1);
reverse(a+1,a+n+1);
ll ans=-INF;
for (int i = 1; i <= n; i++)
{
ll lpos=l1[i],rpos=n+1-r1[n+1-i];
if (a[i]==0)
{
ans = max(0ll, ans);
continue;
}
if (a[i] < 0)
{
if(i==1)//分类讨论下标为0的情况
ans=max(ans,a[i]*query_min(i,rpos,tree2));
else if(lpos==1)//分类讨论下标为0的情况
ans=max(ans,a[i]*(query_min(i,rpos,tree2)-max(0ll,query_max(1, i-1, tree1))));
else
ans = max(ans, a[i]*(query_min(i,rpos,tree2)-query_max(lpos-1,i-1,tree1)));
}
if (a[i]>0)
{
if(i==1)//分类讨论下标为0的情况
ans=max(ans,a[i]*query_max(i,rpos,tree1));
else if(lpos==1)//分类讨论下标为0的情况
ans=max(ans,a[i]*(query_max(i,rpos,tree1)-min(0ll,query_min(1,i-1,tree2))));
else
ans = max(ans, a[i] *(query_max(i,rpos,tree1)-query_min(lpos-1,i-1,tree2)));
}
}
printf("%lld\n", ans);
}