CF1660D Maximum Product Strikes Back 题解

原题传送​​​​​​

思路比较简单就是有点不好想。

数组为空的话乘积为 11,所以我们最终得到的这一串数一定要满足一下条件,不然的话还不如选空串:

  • 没有 00,否则乘积就是 00。
  • 有偶数个负数,否则乘积就是负数。

根据不能选 00 这一条限制,我们可以把 00 当做“分割点”,例如:

1 2 -1 -2 0 2 1 0 -2 -1 2

这串数最优解只能在1 2 -1 -22 1-2 -1 2和空串中,下文为了方便我们管这些组数字叫“数字串”。

我们可以每找到一个“数字串”,来判断是否有偶数个负数。

有偶数个负数就很简单,负负得正,跟目前的最大值做比较,最大值记录的是有这一段里有几个数的绝对值为 22。

如果这一段有奇数个负数,我们要找到这一段最左边和最右边的两个负数,他们俩有可能是同一个数,然后我们就看,是去掉从左边到左边的第一个负数会“损失”的绝对值为 22 的个数少,还是去掉从右边到右边的第一个负数会“损失”的绝对值为 22 的个数少。去掉“损失”少的那一边,留下剩下的和刚才的最大值作比较。

举个例子:

例如我们要看这个“数字串”:1 2 -1 2 1 -2 2 -2 1 2

这一串有奇数个负数。

首先先找到最左边的负数,是第三项,−1−1。然后再找最右边的负数,是第八项,−2−2。接下来比较是删掉1 2 -1损失的小还是删掉-2 1 2损失的小。显然是左边这一串数字包含的绝对值为 22 的数少。我们把左边这一串给删掉,写程序时可以将左边界变成 44。

最后对于这个“数字串”,在有偶数个负数的情况下有最多就有 55 个数的绝对值为 22,跟之前的最大值做比较即可。

代码:
#include<bits/stdc++.h>
using namespace std;
long long a[200005];
long long qzhf[200005];//有几个负数(前缀和)
long long qzh2[200005];//有几个数的绝对值为2(前缀和)
long long da,l,r;
long long f1,f2;//对与每段,第一个负数和最后一个负数
void hanshu(long long zuo,long long you){
    if(zuo>=you&&a[zuo]<=0)return;
    if((qzhf[you]-qzhf[zuo-1])%2==1){//如果有奇数个负数
        f1=zuo;f2=you;//分别从左,右两边开始找负数
        while(a[f1]>0)f1++;//从左边开始,如果这个数不是负数就继续找
        while(a[f2]>0)f2--;//同上,从右边开始找第一个负数
        if(qzh2[you]-qzh2[f2-1]>qzh2[f1]-qzh2[zuo-1])zuo=f1+1;//左边的2少就删掉左边
        else you=f2-1;//否则删掉右边
    }
    //有偶数个负数就不用再改变边界了
    if(qzh2[you]-qzh2[zuo-1]>da){//如果这一段2的个数比之前的最大值多
        da=qzh2[you]-qzh2[zuo-1];//更新最大值
        l=zuo;r=you;//更新左边界
    }
}
int main(){
    long long t,n,zuo;
    cin>>t;
    while(t--){
        cin>>n;
        zuo=1;da=0;//将左边界与最大值初始化
        l=1;r=0;//初始化
        qzh2[0]=0;qzhf[0]=0;//初始化
        for(long long i=1;i<=n;i++){
            cin>>a[i];
            qzh2[i]=qzh2[i-1];qzhf[i]=qzhf[i-1];
            if(abs(a[i])==2)qzh2[i]++;//前缀和预处理
            if(a[i]<0)qzhf[i]++;
        }
        a[n+1]=0;//为了最后不越界,要将a[n+1]设置成0
        qzh2[n+1]=qzh2[n];//将第n+1项也预处理了
        qzhf[n+1]=qzhf[n];
        for(long long i=1;i<=n+1;i++){
            if(a[i]==0){//如果这一项是0
                hanshu(zuo,i-1);
                zuo=i+1;//更新左边界
            }
        }
        cout<<l-1<<" "<<n-r<<endl;//输出
    }
    return 0;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值