在long long范围内,我们经常进行+、-、*、/、%、^运算,其中+、-、/不会有什么问题。
对于乘法和幂运算,我们需要考虑爆掉longlong的情况,而题目也一般会要求你模除一个数,也即a*b%c,a^b%c,但是显然a*b,a^b也一样会爆longlong,因此乘法和幂运算不能直接算,需要一些特殊的方法。
1、 乘法取模运算
实现方法是将一个乘数二进制拆分,另外一个乘数进行多次乘法并取模。
LL mul_mod(LL a,LL b,LL c){
a%=c;
b%=c;
long long ret=0;
while(b)
{
if(b&1){ret+=a;ret%=c;}
a<<=1;
if(a>=c)a%=c;
b>>=1;
}
return ret;
}
2、 幂取模运算
实现方法是将指数进行二进制拆分,运用快速幂求解,中间的乘法用上面的mul_mod运算
LL pow_mod(LL a,LL tmp,LL n){
LL p[32],i=0;
while (tmp>0){
p[i++]=tmp & 1 ? 1 : 0;
tmp=tmp>>1;
}
LL s=1;
for (i--;i>=0;i--){
s=s*s%n;
if (p[i]==1) s=mul_mod(s,a,n);
}
return s;
}
最小公约数的经典求法是欧几里德算法(也即辗转相除法),最小公倍数即为两数乘积除以最大公约数。
需要说明的负数也是有约数的,并且约数的相反数也是一个约数。
假设a<0,则|a|的约数也同样是a的约数,同样的|a|的任意约数也是a的约数。
不失一般性,我们通常直接认为约数为正数。
LL gcd(LL a,LL b){
LL c;
if (a==0) return 1;
if (a<0) return gcd(-a,b);
if (b<0) return gcd(a,-b);
if (a<b){
LL x=a;
a=b;
b=x;
}
c=b;
while (b!=0){
c=a%b;
a=b;
b=c;
}
return a;
}