蓝桥杯2021年第十二届国赛真题-和与乘积

样例输入

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

 

  • 15
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值