[51nod1254]最大子段和 V2

时间:2023-03-09 06:17:04
[51nod1254]最大子段和 V2

  N个整数组成的序列a[1],a[2],a[3],…,a[n],你可以对数组中的一对元素进行交换,并且交换后求a[1]至a[n]的最大子段和,所能得到的结果是所有交换中最大的。当所给的整数均为负数时和为0。

  例如:{-2,11,-4,13,-5,-2, 4}将 -4 和 4 交换,{-2,11,4,13,-5,-2, -4},最大子段和为11 + 4 + 13 = 28。
 Input
  第1行:整数序列的长度N(2 <= N <= 50000)
  第2 - N + 1行:N个整数(-10^9 <= A[i] <= 10^9)
 Output
  输出交换一次后的最大子段和。

  先考虑与左边的数字交换的情况。

  枚举交换位置x,把交换后的段拆成x左边和x右边两部分算。

  需要事先计算出前缀和、后缀和、后缀和的后缀最小值、(前缀和 - 前缀最大值)的前缀最小值。

  和右边的数交换同理。。

 #include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<queue>
#include<cmath>
#define ll long long
#define ui unsigned int
#define ull unsigned long long
using namespace std;
const int maxn=,modd=;
ll mn1[maxn],mn2[maxn],_mn1[maxn],_mn2[maxn],pr[maxn],af[maxn],ans;
int prmx[maxn],afmx[maxn],a[maxn];
int i,j,k,n,m; int ra,fh;char rx;
inline int read(){
rx=getchar(),ra=,fh=;
while((rx<''||rx>'')&&rx!='-')rx=getchar();
if(rx=='-')fh=-,rx=getchar();
while(rx>=''&&rx<='')ra*=,ra+=rx-,rx=getchar();return ra*fh;
} int main(){
n=read();prmx[]=afmx[n+]=-1e9;
for(i=;i<=n;i++)a[i]=read(),pr[i]=pr[i-]+a[i],prmx[i]=max(prmx[i-],a[i]);
for(i=n;i;i--)af[i]=af[i+]+a[i],afmx[i]=max(afmx[i+],a[i]); // mn1[1]=0,mn2[1]=pr[1];
for(i=;i<=n;i++)
mn1[i]=min(mn1[i-],pr[i]-prmx[i]),
mn2[i]=min(mn2[i-],pr[i]);
// _mn1[n]=0,_mn2[n]=af[n];
for(i=n;i;i--)
_mn1[i]=min(_mn1[i+],af[i]-afmx[i]),
_mn2[i]=min(_mn2[i+],af[i]);
for(i=;i<=n;i++)
//i=15,//printf(" %lld-%lld %lld-%lld\n",af[i+1],_mn2[i+1],pr[i-1],mn1[i-1]),
ans=max(ans,(af[i+]-_mn2[i+])+(pr[i-]-mn1[i-])),
ans=max(ans,(pr[i-]-mn2[i-])+(af[i+]-_mn1[i+])),
ans=max(ans,pr[i]-mn2[i]);
printf("%lld\n",ans);
}