题解CF1660D
CF1660D Maximum Product Strikes Back
题意
有\(n\)个元素的数组,每个元素满足\(|a_i|\le2\),在左右区间各删除连续若干个数(可以为0),使得剩下的区间元素乘积值最大(若剩下区间空数组,值为1),输出左边和右边删除的数量,若有多种答案,输出任何一种。
思路
注意到0一定不能包含在答案数组区间中,所以最大乘积是一段不包含0的区间,故可以把区间分成若干段。
然后,对于每一段区间,统计负数个数,若为偶数,则该区间最大乘积为全部相乘,若为奇数,从左往右枚举直到第一个负数,从右往左枚举直到第一个负数,比较两种情况的最大值。
最后,比较所有区间乘积,记录最大的那个区间,然后根据左右边界输出答案。
另外本题有一个坑点,不能直接比较区间乘积,因为答案会高达\(2\)的次幂,会爆longlong,注意到\(|a_i|\le2\),所以我们考虑优化,不比较乘积,而是比较每个区间\(2\)和\(-2\)的个数,显然,个数越多的区间乘积自然越大。
代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll n,t,a[200005],b[200005];
signed main()
{
ios::sync_with_stdio(false);
cin>>t;
while(t--)
{
vector<ll>v;
cin>>n;
for(ll i=1;i<=n;++i)cin>>a[i];
ll w=1,u=0;
while(w<=n)//将数组分成若干个区间,我这里是分别把左右区间放进vector里,也可以直接用pair
{
if(a[w]!=0&&(w==1||a[w-1]==0))v.push_back(w);
if(a[w]==0&&a[w-1]!=0)v.push_back(w-1);
++w;
}
if(a[n]!=0)v.push_back(n);
if(v.size()==0)cout<<n<<" "<<0<<endl;//特判全是0的情况
else
{
for(ll i=0;i<v.size();i+=2)
{
ll sum=0,sum2=0;//sum表示负数的数量,sum2表示2和-2的数量
for(ll j=v[i];j<=v[i+1];++j)
{
if(a[j]<0)++sum;
if(a[j]==2||a[j]==-2)++sum2;
}
if(sum%2==0)b[++u]=sum2;
else
{
ll now=v[i],r=0,l=0,now2=v[i+1];//r和l记录要删去的2和-2的个数
while(a[now]>0)//从左往右枚举
{
if(a[now]==2)++r;
++now;
}
if(a[now]==-2)++r;
while(a[now2]>0)//从右往左枚举
{
if(a[now2]==2)++l;
--now2;
}
if(a[now2]==-2)++l;
sum2-=min(l,r);
b[++u]=sum2;//b数组存储每个区间2和-2的个数
if(l>=r)v[i]=now+1;
else v[i+1]=now2-1;//选择乘积最大的区间
}
}
ll xb,ma=-1;
for(ll i=1;i<=u;++i)
{
if(b[i]>ma)
{
ma=b[i];
xb=i;
}
}//找到2和-2个数最多的区间,即为乘积最大的区间
cout<<v[xb*2-2]-1<<" "<<n-v[xb*2-1]<<endl;
}
}
return 0;
}

浙公网安备 33010602011771号