2013寒假练习 快速幂三弹连发 A^B mod C v1.0-3.0

1044:A^B mod C v1.0  (1<=A,B<2^62, 1 <= C <= 10^9)

  http://acm.bit.edu.cn/mod/programming/view.php?a=530

1045:A^B mod C v2.0  (1<=A,B,C<2^62)                         

  http://acm.bit.edu.cn/mod/programming/view.php?a=531

1056:A^B mod C v3.0 1<=A,C<=1000000000,1<=B<=10^1000000).

http://acm.bit.edu.cn/mod/programming/view.php?a=542

三题题意相同:求A^B mod C的值,但数据范围不同。

第一题最基础的快速幂无须多言

#include<iostream>
int main()
{
	__int64 a,b,c,t;
	while(~scanf("%I64d%I64d%I64d",&a,&b,&c))
	{
		a%=c,t=1;
		while(b-1)
		{
			if(b%2) t=t*a%c;
			b/=2,a=a*a%c;	
		}
		printf("%I64d\n",a*t%c);
	}
	return 0;
}

第二题 C的范围变成了long long ,导致快速幂中将会有两个long long 相乘超long long的情况。

故写一个函数在不超long long的情况下计算 a和b相乘mod c。方法和快速幂一个思路!(快速乘?) 若b为偶数,mul(a,b)=mul(a+a,b/2) 若b为偶数mul(a,b)=mul(a+a,b/2)+a。

把乘法变成加法来做,这样就不会超long long 了=w=

#include<iostream>
__int64 a,b,c,t;
__int64 mul(__int64 a,__int64 b) //a*b%c的二分做法:防止超long long
{
	__int64 t=0;
	while(b>1)
	{
		if(b%2) t=(t+a)%c;
		a=(2*a)%c,b/=2;
	}
	return (t+a)%c;
}
int main()
{
	
	while(~scanf("%I64d%I64d%I64d",&a,&b,&c))
	{
		a%=c,t=1;
		while(b-1)
		{
			if(b%2) t=mul(t,a);
			b/=2,a=mul(a,a);	
		}
		printf("%I64d\n",mul(t,a));
	}
	return 0;
}

第三题更神奇了,B变成了100000位的大数。用高精度计算吃力不讨好(超时),这时又要用到数论结论。幂运算然后取模随着指数的增加是有循环节的,有定理

证明详见http://hi.baidu.com/aekdycoin/item/e493adc9a7c0870bad092fd9

那么就可以用b%phi(c)+phi(c)来代替b做快速幂。如何求大数b%phi(c)呢?如求1234567654321%101,先1模101,然后乘以10再+2,再模,再乘以10再+3,即可。

注意公式中的限制条件x>=phi(c)

#include<iostream>
char b[1000005];
__int64 phi(__int64 a)  //求欧拉函数值
{
	__int64 ans=a;
	for(int i=2;i*i<=a;i++)
	{
		if(a%i==0)
		{
			ans=ans/i*(i-1);
			while(a%i==0) a/=i;
		}
	}
	if(a>1) ans=ans/a*(a-1);
	return ans;
}
int main()
{
	__int64 a,c,t,phic,newb,len,i; 
	while(~scanf("%I64d",&a))
	{
		bool flag=false; //b是否大于phic
		scanf("%s%I64d",b,&c);
		a%=c,t=1,phic=phi(c),len=strlen(b),newb=0;
		for(i=0;i<len;i++)   //求b%phi(c)
		{
			newb=newb*10+(b[i]-'0');
			if(newb>=phic) newb%=phic,flag=true;
		}
		if(flag==true) newb+=phic;  //新的指数为b%phi(c)+phi(c)
		while(newb-1)
		{
			if(newb%2) t=t*a%c;
			newb/=2,a=a*a%c;	
		}
		printf("%I64d\n",a*t%c);
	}
	return 0;
}


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值