原题传送
思路比较简单就是有点不好想。
数组为空的话乘积为 11,所以我们最终得到的这一串数一定要满足一下条件,不然的话还不如选空串:
- 没有 00,否则乘积就是 00。
- 有偶数个负数,否则乘积就是负数。
根据不能选 00 这一条限制,我们可以把 00 当做“分割点”,例如:
1 2 -1 -2 0 2 1 0 -2 -1 2
这串数最优解只能在1 2 -1 -2
,2 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;
}