样例输入
4 1 3 2 2
样例输出
6
拿到这个题的时候想了好久,只是感觉对于这个题来说,数字1是一个十分特殊的数,正是由于有了数字1的存在才会使一段区间数的乘积等于数的和,因为如果数字>2的话,很明显乘积增长的速度要大于累加增长的速度,有了1之后,乘积不再增加,和开始慢慢增加,和才有了追上乘积的可能,然后想到先把n个数中所有不包含1的子段之和,子段之积求出来,进行比较,如果相等,构成一种答案,如果和小于乘积,就拿当前这段数左右两边的1来凑,但是突然发现不应该用整个不,包含1的子段去求解,因为子段的前面和1相邻的一部分和前面的1有可能构成一个解,后面也可以构成一组解,然后,我陷入了沉思,知道我看到了这位大佬的博客:(96条消息) 蓝桥杯2021年第十二届国赛真题-和与乘积_CTYring的博客-CSDN博客,
由于大佬没怎么写注释,所以也是在研究了好一段时间后,终于大彻大悟,所以我决心根据大佬的思路写一篇比较详细的博客,来拯救一下后来被此题所困的难兄难弟们。
解题思路:在下面的代码中首先我们会对输入的k个数据进行一个类似于压缩的操作,我们把原数列中所有的1都去掉了,转而用下面几个数组来维护原序列中的信息,onecnt[i]表示去掉所有的1之后,第i个数字前面连续的1的个数,sum[i]表示从第一个非1的数到第i个非1的数,并且包含了第i个非1的数之前所有的1的数的和,num[i]表示第i个非1的数是什么,进行处理后,我们得到了一个长度为n-1的sum[],num[]数组,随后我们求出所有数字的和res,然后我们从第一个非1的数开始枚举,计算当前数字和它后面非1的数的乘积,在计算的过程中,每乘一个数都要判断是否可以构成一组解:
如果说当前的乘积已经大于所有数的和,那一定找不到对应的一组累加和,直接退出即可。
对于乘积小于所有数的和的情况,首先计算出乘积与和的差值d,如果d=0,则构成一组解,如果说乘积大于差值 ,并且差值d<=当前区间左右两边相邻的连续的1的和,那此时通过加上左右两边的1也可以构成解。
最后的解就是原序列的长度+符合上述分析的解的个数。
上代码:
#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
using namespace std;
const int N=2e5+10;
long long sum[N],num[N],onecnt[N];
int n=1;
int k;
int main()
{
cin>>k;
for(int i=1;i<=k;i++)
{
int temp;
cin>>temp;
if(temp==1)
{
sum[n]++;
onecnt[n]++;
}
else
{
sum[n]+=sum[n-1]+temp;
num[n]=temp;
n++;
}
}
long long res=sum[n-1]+onecnt[n];//计算所有数字的和,因为最后有可能有一段连续的1,所以要加上onecnt[n]
long long ans=k;//初值为原序列的长度,因为原序列的每一个元素都构成一组解
for(int i=1;i<n;i++)
{
long long p=num[i];
for(int j=i+1;j<n;j++)
{
p*=num[j];
if(p>res)
break;
long long d=p-sum[j]+sum[i-1]+onecnt[i];
if(d==0)
ans++;
else if(onecnt[i]+onecnt[j+1]>=d&&d>0)//前后面所有的1加起来大于差值
{
long long left=min(d,onecnt[i]);
long long right=min(d,onecnt[j+1]);
ans+=left+right-d+1;//left+right-d可以理解成左右两边最多可以空出这么些1来,那么所有情况就是这个数+1,因为也可以不空出来
}
}
}
cout<<ans<<endl;
return 0;
}
又是几乎一个晚上。。。。,菜哭QAQ