快速幂算法 牛客小白月赛1-C 分元宵

牛客小白月赛1的C题

链接:https://ac.nowcoder.com/acm/contest/85/C
来源:牛客网

毕竟是元宵节,晚上还是要吃几个元宵。 Etéreo 家可是个大家庭,元宵的数量,甚至是餐具的数量,都多的惊人。现在,爱数学的 Etéreo 又来问你有趣的数学题了,快来秒掉它! Etéreo 家里有 ς 种元宵馅, ϑ 种元宵皮,每个元宵可以选择任意一种元宵馅和任意一种元宵皮。同时有ϖ 张桌子,每张桌子上放了 ϱ 只碗,每只碗能放一只元宵。每只碗都要装一只元宵。Etéreo 会告诉你 ς,ϑ,ϖ,ϱ 的值,想请问你有多少种装元宵的方式。答案对Λ 取模。
两种方式被认为是不同的当且仅当至少有一只碗存在于两种方式的同一个位置但是里面有至少一个元宵不同。
两个元宵被认为是不同的当且仅当元宵馅不同或者元宵皮不同。
碗和桌子都是有编号的,但是你不能改变碗或桌子的编号。
你可以认为碗和桌子都是固定的,你只能改变元宵的种类和位置。

0≤ς,ϑ≤1018
0≤ϖ,ϱ≤106
1≤Λ≤1,000,000,007

这道题看起来不太难,首先有ϖ*ϱ只碗,元宵一共有ς*ϑ种,所以最后的答案为(ϖ*ϱ)ς*ϑ种,再对Λ取模即可

但是指数的结果是非常大的,没有数据结构能存下,而且指数过大,很有可能会超时,那么如何解决这一问题?

快速幂

字面意思,就是快速求幂

先写一下最简单的求法吧。

在最开始学习C++时,我们首先接触到的方法是cmath中的pow函数或者循环计算

例如求2^10

#include<iostream>
#include<cmath>
using namespace std;
int main()
{
    cout<<pow(2,10);
}
#include<iostream>
using namespace std;
int main()
{
    int ans=1;
    for(int i=1;i<=10;i++)
        ans*=2;
    cout<<ans;
}

pow方法简单,但是效率比循环要低。

循环效率也不高,计算一个数字的n次幂,就需要循环n次,复杂度为O(n)。

做最前面的那道算法题时这两种办法肯定是不行的,如何优化?

例如求2^8

210

=2*2*2*2*2*2*2*2*2*2

=(2*2)*(2*2)*(2*2)*(2*2)*(2*2)

=4^5

这时我们需要循环的次数就减少了一半,只需要五次

这是我们的第一次优化,可以发现,底数变成了原来的平方,指数变成了原来的一半

如果是10000次循环,经过一次优化就变成了5000次,优化的程度就很大了

继续这么优化

45=42.5

就发现一个问题,如果是奇数次,指数除以二之后出现了小数部分,那么如何解决?

提出来其中的一个,指数又变成了偶数

45=44*41

那么就可以继续优化了

45=44*41=162*41=2561*41

所以这个算法的思路是

首先与循环算法一样,定义一个结果变量result=1

如果指数是偶数,那么底数平方,指数除以2

如果指数是奇数,那么提出来其中一项并乘进结果中,指数减一
复杂度为O(logn)

代码如下

	long long fastpow1(long long base,long long power)
    {
        int result = 1;
        while(power>0)
        {
            if(power%2==0)
            {
                base=base*base;
                power/=2;
            }
            else
            {
                result*=base;
                power--;
               	//此时power变为偶数,可以进行与power为偶数时相同的操作
                power/=2;
                base=base*base;
            }
        }
        return result;
    }

可以看到,有相同的代码语句,所以进行如下缩减

long long fastpow2(long long base,long long power)
{
    int result = 1;
    while(power>0)
    {
        if(power%2)		//如果power为奇数
        {
            result*=base;
            power--;
        }
        power/=2;
        base=base*base;
        }
    }
    return result;
}

因为power为整数类型,如果为奇数可以自动舍去0.5

例如power=3,power/2结果为1,与power–;power/=2;结果相同

所以继续优化

long long fastpow3(long long base,long long power)
{
    int result = 1;
    while(power>0)
    {
    	if(power%2)
    		result*=base;
        power/=2;
    	base=base*base;
    }
    return result;
}

前面说到,指数的结果非常大,所以常用到取模运算

这里用到性质

(a*b)%c=(a%c*b%c)%c

所以代码如下:

long long fastpow4(long long base,long long power,int mod)
{
    int result = 1;
    while(power>0)
    {
    	if(power%2)
    		result=(result*base)%mod;
        power/=2;
    	base=(base*base)%mod;
    }
    return result;
}

这大概就是最终的写法了,但是其中的一些地方我们可以改为位运算,速度更快

long long  fastPower5(long long  base, long long  power, long long  mod)
{
	long long  result = 1;
	while (power > 0)
	{
		if (power & 1)			//如果power为奇数
			result = (result * base) % mod;
		power >>= 1;
		base = (base * base) % mod;
	}
	return result;
}

总结一下

pow效率较低,但是适用于指数为小数的情况

否则不建议使用

循环复杂度为O(n),快速幂算法复杂度为O(logn)

指数越大,效果越明显

回到分元宵的问题

最大的坑是输入的四个数据可能有0,需要单独判断,否则通过率为98%。

最后,题目的答案

#include<iostream>
using namespace std;
typedef long long ll;
ll A;
ll fastPower(ll base, ll power);
int main()
{
	ll c, o, w, e;
	cin >> c >> o >> w >> e >> A;
	if (c == 0 || o == 0 || w == 0 || e == 0)
	{
		cout << 0;
		return 0;
	}
	cout << fastPower(fastPower(((c % A) * (o % A)) % A, w), e);
}
ll fastPower(ll base, ll power)
{
	ll result = 1;
	while (power > 0)
	{
		if (power & 1)
			result = (result * base) % A;
		power >>= 1;
		base = (base * base) % A;
	}
	return result;
}
posted @ 2020-06-04 18:12  Francis-xsc  阅读(40)  评论(0编辑  收藏