题解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;
}
posted @ 2022-04-04 19:56  carnation13  阅读(61)  评论(0)    收藏  举报