FOJ 1759 Super A^B mod C

       更新:2014-02-10    凌晨

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

       来源:http://acm.fzu.edu.cn/problem.php?pid=1759

       概述:计算a^b mod c,但b是个相当大的数,可以达到100万位。

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

理论分析

       首先要知道欧拉函数,可以参看FOJ 1012题的解答。

       然后,这里直接套用以下公式:


       注意,有的文章提到公式使用的条件是b≥φ(c),其实上述公式是没有这个限制的。只不过b<φ(c)时,如果利用公式计算,反而增大了幂值,在最终采用逐次平方法求解时运算量变大。

       公式的证明,可以参阅这两篇文章:

       http://www.narutoacm.com/archives/a-pow-b-mod-m/

       http://hi.baidu.com/aekdycoin/item/e493adc9a7c0870bad092fd9

       我也补充点对该公式的理解:当a与c互素时,用欧拉定理


截图来自:陈景润,《初等数论II》第五章,百度网盘下载

就能很容易证明


       当a与c不互素时,后面的指数部分,就多加了φ(c),变成b%φ(c)+φ(c)。即上述的“求幂大法”,我认为是在欧拉定理的基础上,进行的推广应用。

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

编程分析

       1、欧拉函数直接套用FOJ1012的模板。

       2、b是个大数,b%φ(c)要使用大数取余运算,这个其实不难。我把大数存在字符串str,把最终值存在b,在下述代码中第42行,一行for循环就可以完成该运算。

       3、最终得到的b,一定是10亿以内的数,直接采用普通的快速幂模模板——逐次平方法求解。

#include <cstdio>
#include <cstring>
using namespace std;

typedef __int64 i64;

i64 pow(i64 a, i64 b, i64 c)
{
	i64 p = 1;
	while (b)
	{
		if (b&1) p = p * a % c;
		b >>= 1;
		a = a * a % c;
	}
	return p;
}

i64 euler(i64 n)  
{  
    i64	i, ans = 1;  
    for (i = 2; i*i <= n; i++)  
    {  
        if (n%i==0)  
        {  
            ans *= i-1, n /= i;  
            while (n%i==0) ans *= i, n /= i;  
        }  
    }  
    if (n!=1) ans *= n-1;  
    return ans;  
}  

int main()
{
	i64		a, b, c;
	char	str[1000001];
	
	while (scanf("%I64d%s%I64d", &a, &str, &c) != EOF)
	{
		int len = strlen(str), phi = euler(c), i;
		for (b = i = 0; i < len; i++) b = (b*10 + str[i] - '0') % phi;
		b += phi;
		printf("%I64d\n", pow(a, b, c));
	}
	return 0;
}

       花絮:我试着把b += phi;删掉,然后提交一次,发现AC了,说明测试数据还不够严谨,这句话本来是必须的,否则就直接变成欧拉定理了,而欧拉定理成立的条件是a与c互素。反例:输入的a b c分别为4 1 2。如果去掉第43行代码,输出为1,而实际应该输出0。

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

效率分析

       上述代码中,没有对b的范围进行分类求解,因为那样会带来不必要的编程麻烦,效率未必会更高。

       上述代码AC用时125ms。如果加上#include <cstdlib>,使用atof和atoi函数,把主函数改成如下形式,则用时140ms,效率不减反增了。这里写这代码主要是为了学习下atof和atoi函数。

int main()
{
	i64		a, b, c;
	char	str[1000001];
	
	while (scanf("%I64d%s%I64d", &a, &str, &c) != EOF)
	{
		int len = strlen(str), phi = euler(c), i;
		if (atof(str) > phi) 
		{
			for (b = i = 0; i < len; i++) b = (b*10 + str[i] - '0') % phi;
			b += phi;
		}
		else b = atoi(str);
		printf("%I64d\n", pow(a, b, c));
	}
}
       从效率角度讲,可以这样优化算法:

int main()
{
	i64		a, b, c;
	char	str[1000001];
	
	while (scanf("%I64d%s%I64d", &a, &str, &c) != EOF)
	{
		int len = strlen(str), phi = euler(c), i;
		if (len > 10) 
		{
			for (b = i = 0; i < len; i++) b = (b*10 + str[i] - '0') % phi;
			b += phi;
		}
		else b = atoi(str);
		printf("%I64d\n", pow(a, b, c));
	}
}
       不过这段代码用时也是125ms,并没有太大的提高。比赛中,只要能AC,也不会在意这一点点时间差,既然公式能通用,就不必要分类讨论了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值