题意:
给一个长度为 n n n的数组 ( n ≤ 100000 ) (n\leq100000) (n≤100000),对任意 i ∈ [ 1 , n ] i\in[1,n] i∈[1,n],有 a i ∈ { − 2 , − 1 , 0 , 1 , 2 } a_{i}\in\{-2,-1,0,1,2\} ai∈{−2,−1,0,1,2},可以删去数组的前任意多个和后任意多个。若要使删完之后的数组的元素积最大,求前面删多少,后面删多少
方法:
这是一个求最大子段积的问题,我们先抛开一切其他问题,仅仅讨论一个数组的最大子段积如何求,和最大子段和类似,设 d p [ i ] dp[i] dp[i]为以 i i i为结尾的最大子段积是多少,那么转移应该是从 d p [ i − 1 ] dp[i-1] dp[i−1]转移来的,对于最大子段和,有 d p [ i ] = m a x ( d p [ i − 1 ] + a [ i ] , a [ i ] ) dp[i]=max(dp[i-1]+a[i],a[i]) dp[i]=max(dp[i−1]+a[i],a[i]),同样,我们每次对 a [ i ] a[i] a[i]的决策也仅有两种,加入前面的子段,或者单独成段,但乘法存在负负得正,可能 a [ i ] < 0 a[i]<0 a[i]<0,而前面的最小子段和 × a [ i ] \times a[i] ×a[i]可能大于最大子段和 × a [ i ] \times a[i] ×a[i],因此,求最大子段积需要维护两个量,一个是最大子段积,一个是最小子段积,令他们分别是 f [ i ] , g [ i ] f[i],g[i] f[i],g[i],有
f [ i ] = m a x ( f [ i − 1 ] ∗ a [ i ] , a [ i , g [ i − 1 ] ∗ a [ i ] ) f[i]=max(f[i-1]*a[i],a[i,g[i-1]*a[i]) f[i]=max(f[i−1]∗a[i],a[i,g[i−1]∗a[i])
g [ i ] = m i n ( g [ i − 1 ] ∗ a [ i ] , a [ i ] , f [ i − 1 ] ∗ a [ i ] ) g[i]=min(g[i-1]*a[i],a[i],f[i-1]*a[i]) g[i]=min(g[i−1]∗a[i],a[i],f[i−1]∗a[i])
对于题目的范围,最大的子段积能达到 2 100000 2^{100000} 2100000,爆 l o n g l o n g longlong longlong,而这里只存在 − 2 , − 1 , 0 , 1 , 2 {-2,-1,0,1,2} −2,−1,0,1,2,所以可以封装一个结构体,来表示 a 2 b a2^b a2b,重载运算符来计算。
并且题目问的是前面需要删多少个,那么可以多维护两个 f t o t [ i ] , g t o t [ i ] ftot[i],gtot[i] ftot[i],gtot[i],来表示以 i i i结尾的子段长度是多少
#include<bits/stdc++.h>
#define ll long long
using namespace std;
struct newint
{
int a,b;
void trans(int tmp)
{
a=(tmp>=0);
tmp=abs(tmp);
if(tmp==2) b=1;
else if(tmp==1) b=0;
else b=-1;
}
void solve(){if(b==-1) a=1;}
void operator=(const newint& x)
{
a=x.a; b=x.b;
solve();
}
bool operator==(const newint& x) const
{
return a==x.a&&b==x.b;
}
bool operator<(const newint& x) const
{
if(a>x.a) return false;
else if(a<x.a) return true;
// printf("b=%d,x.b=%d\n",b,x.b);
if(a==0) return b>x.b;
return b<x.b;
}
bool operator!=(const newint& x) const
{
return !(*this==x);
}
bool operator>(const newint& x) const
{
return *this!=x&&!(*this<x);
}
newint operator*(const newint& x) const
{
newint ret;
ret.a=a^x.a^1;
if(b==-1||x.b==-1) ret.b=-1;
else ret.b=b+x.b;
ret.solve();
return ret;
}
};
int n,ftot[200005],gtot[200005];
newint a[200005],f[200005],g[200005];
void work()
{
cin>>n;
for(int i=1;i<=n;i++)
{
int tmp; scanf("%d",&tmp);
a[i].trans(tmp);
}
newint max1={-1,-1};
for(int i=1;i<=n;i++)
{
if(f[i-1]*a[i]>a[i])
{
f[i]=f[i-1]*a[i];
ftot[i]=ftot[i-1]+1;
}
else f[i]=a[i],ftot[i]=1;
if(a[i]*g[i-1]>f[i]) f[i]=a[i]*g[i-1],ftot[i]=gtot[i-1]+1;
if(g[i-1]*a[i]<a[i])
{
g[i]=g[i-1]*a[i];
gtot[i]=gtot[i-1]+1;
}
else g[i]=a[i],gtot[i]=1;
if(a[i]*f[i-1]<g[i]) g[i]=a[i]*f[i-1],gtot[i]=ftot[i-1]+1;
max1=max(max1,f[i]);
}
if(max1<(newint){1,0}||max1==(newint){1,0})
{
printf("%d %d\n",0,n);
return;
}
for(int i=1;i<=n;i++)
{
if(f[i]==max1)
{
printf("%d %d\n",i-ftot[i],n-i);
return;
}
}
}
int main()
{
f[0].trans(1); g[0].trans(1);
int t; cin>>t;
while(t--) work();
return 0;
}