蓝桥杯 ALGO-999 数的潜能

题目:

问题描述

  将一个数N分为多个正整数之和,即N=a1+a2+a3+…+ak,定义M=a1*a2*a3*…*ak为N的潜能。
  给定N,求它的潜能M。
  由于M可能过大,只需求M对5218取模的余数。

输入格式

  输入共一行,为一个正整数N。

输出格式

  输出共一行,为N的潜能M对5218取模的余数。

样例输入

10

样例输出

36

数据规模和约定

  1<=N<10^18

分析:

        我认为这道题最大的难点就在与题目条件没说全,实际上是要求M尽量大的。

        好的,那么知道了这个,我们来浅浅的分析一下。

        一个数,例如1、2、3、4、5、6、7、8

        1是没办法分解的,结果之能为1。

        2是分解后就不符合条件的,比如2=1+1,但1*1=1,M太小了,不如不分解。

        3和2一样。

        4可以分解为1+3,2+2,但1*3=3,2*2=4,所以,我们会把4分解为2+2

        5可以分解为,1+4,2+3,我们会分解为2+3

        6可以分解为,1+5,2+4,3+3,为了让M更大,其中对5,4进一步分解,结果为1+2+3,2+2+2,3+3,我们会选择3+3,这样M是9

        7可以分解为,1+6,2+5,3+4,为了让M更大,其中对4,5,6进一步分解,结果为1+3+3,2+2+3,3+2+2,我们会选择2+2+3,这样M是12

        8可以分解为,1+7,2+6,3+5,4+4为了让M更大,其中对4,5,6,7进一步分解,结果为1+3+2+2,2+3+3,3+2+3,2+2+2+2我们会选择3+2+3,这样M是18

        ......

        于是,我们将会发现一个简单的规律,

        规律是,如果我们想要M尽量大,就分解这个数,先用3分解。即N/3,看看有几个3,然后看看余数。

        若余数N%3==2,则M=pow(3,N/3)*2;

        若余数N%3==1,则M=pow(3,N/3-1)*4;

        若余数N%3==0,则M=pow(3,N/3)

然后将M对5218取模就完了。

第一版算法代码:

由于M太大,long long int 也不够,所以我们可以牺牲时间来优化空间,即每次乘法后立即对5218取模。于是有了一下第一版算法

(该算法在蓝桥官网时间超限,仅60分)

#include <iostream>

using namespace std;

int main() 
{
	//已知1<=N<10^18
	//那么用long long int 就够了。
	long long int n;
	
	scanf("%lld",&n);
	int res=1;
	
	int pow=n/3;
	
	if(n==1)
		res=1;
	else if(n==2)
		res=2;
	else
	{
		if(n%3==0)
		{
			for(int i=0;i<pow;i++)
			{
				res*=3;
				//为了防止res太大,那就每次都对5218取模,反正也没影响
				res=res%5218; 
			}
		}
		if(n%3==1)
		{
			for(int i=0;i<pow-1;i++)
			{
				res*=3;
				//为了防止res太大,那就每次都对5218取模,反正也没影响
				res=res%5218; 
			}
			res*=4;
			res=res%5218;
		}
		if(n%3==2)
		{
			for(int i=0;i<pow-1;i++)
			{
				res*=3;
				//为了防止res太大,那就每次都对5218取模,反正也没影响
				res=res%5218; 
			}
			res*=2;
			res=res%5218;
		}
	}
	printf("%d",res);
	return 0;
}

优化算法: 

        即然时间超了,那么就优化代码。我想到了快速幂运算,因为n非负,所以我们大可以很放心的使用幂运算。

幂运算原理与模板如下:

模板:

int pow(int x, int y)
{
	int ans = 1;
	while(y)
	{
		if(y&1)	ans = ans*x%p;
		x = x*x%p;
		y >>= 1;
	}
	return ans;
}

原理:

        看模板来说,对没接触过的人来说可能比较抽象,但实际其实很简单。

        1.上述代码ans=1,因为任何数的0次幂为1.

        2.就像3^4=9^2一样,3^5=(9^2)*3,我们可以看出,再幂比较大时,我们可以增加底数让幂变小,从而减少幂运算次数,进而提高代码效率。

        3.若幂为偶数,则直接底数求平方,幂为原来的一半。若幂为奇数,则幂减1,然后想幂为偶数运算,在最后在乘一个底数。

        4.因此,if(y&1)实际是y的二进制数与1相与,即判断y最低为是否为1,这样就能知道y是奇数还是偶数。

        5.模运算的一个性质:如果我们对两个数分别取模,然后再将它们的乘积取模,得到的结果与先将它们相乘再取模是相同的。这个性质可以用数学公式表示为:(a * b) % m = ((a % m) * (b % m)) % m,所以,为了防止结果过大,我们在过程中取模,不影响结果的。所以可以有

ans = ans*x%p;

        6.y >>= 1;,即将y右移一位,那么y就相当于被除了2.

代码:

#include <iostream>

using namespace std;

int quick_pow(int a,long long int b,int mod)
{
	int res=1;
	while(b>0)
	{
		if(b&1)
		{
			res=res*a%mod;
		}
		a=a*a%mod;
		b=b>>1;
	}
	return res;
} 

int main() 
{
	//已知1<=N<10^18
	//那么用long long int 就够了。
	long long int n;
	
	scanf("%lld",&n);
	int res=1;
	
	int pow=n/3;
	
	if(n==1)
		res=1;
	else if(n==2)
		res=2;
	else
	{
		if(n%3==0)
		{
			res=quick_pow(3,n/3,5218);
		}
		if(n%3==1)
		{
			res=quick_pow(3,n/3-1,5218);
			res=res*4%5218;
		}
		if(n%3==2)
		{
			res=quick_pow(3,n/3,5218);
			res=res*2%5218;
		}
	}
	printf("%d",res);
	return 0;
}

        

  • 32
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值