显然两个数要对答案产生贡献,二者之间的数的个数必为奇数,如果是偶数,就不能仅删除中间的部分。
手模后发现,设对答案产生贡献的数为 a p 1 , a p 2 , … , a p k a_{p_1},a_{p_2},\dots,a_{p_k} ap1,ap2,…,apk, p 1 ≡ p 2 ≡ ⋯ ≡ p n ( m o d 2 ) p_1\equiv p_2\equiv\dots\equiv p_n\pmod 2 p1≡p2≡⋯≡pn(mod2)。
所以最优策略是在奇数或偶数位置选全部正数,然后确定了选的数后,把相邻两个数之间的连续段删去即可,发现中间的最小操作次数就是连续段长度除以 2 2 2,两边的段单独求。
特判序列全部都是负数的情况。
时间复杂度 O ( n ) O(n) O(n)
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define dis(x) (x>1?(x+1>>1):0)
constexpr ll INF=1e18;
const int N=1e6+1;
int n,cnt1,cnt2;
ll ans1,ans2,a[N];
struct node
{
ll x,i;
}b1[N],b2[N];
int main()
{
freopen("friends.in","r",stdin);
freopen("friends.out","w",stdout);
cin.tie(0)->sync_with_stdio(0);
int fl=0;
cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i];
if(i&1) b1[++cnt1]={a[i],i};
else b2[++cnt2]={a[i],i};
if(a[i]>0) fl=1;
}
if(!fl){
ll maxn=-INF,maxi;
for(int i=1;i<=n;i++){
if(maxn<a[i]){
maxn=a[i];
maxi=dis(i)+dis(n-i+1);
}
else if(maxn==a[i]) maxi=min(maxi,1ll*dis(i)+dis(n-i+1));
}
cout<<maxn<<"\n"<<maxi;
return 0;
}
ll maxi=-INF,mini=INF,sum=0;
for(int i=cnt1;i>=1;i--){
if(b1[i].x<=0) continue;
sum+=b1[i].x;
maxi=max(maxi,b1[i].i);
mini=min(mini,b1[i].i);
}
ans1=sum,ans2=(maxi-mini)/2+dis(mini)+dis(n-maxi+1);
sum=0,maxi=-INF,mini=INF;
for(int i=cnt2;i>=1;i--){
if(b2[i].x<=0) continue;
sum+=b2[i].x;
maxi=max(maxi,b2[i].i);
mini=min(mini,b2[i].i);
}
if(ans1<sum) ans1=sum,ans2=(maxi-mini)/2+dis(mini)+dis(n-maxi+1);
else if(ans1==sum) ans2=min(ans2,(maxi-mini)/2ll+dis(mini)+dis(n-maxi+1));
cout<<ans1<<"\n"<<ans2;
}