一、快速幂
快速幂求(a^b)%n。
1.递归实现
int quickpow(int a,int b,int c)
{
if(b==1) return a;
if(b%2==0)
{
int t=quickpow(a,b/2,n);
return t*t%n;
}
else
{
int t=quickpow(a,b/2,n);
t=t*t%n;
t=t*a%n;
return t;
}
}
2.非递归实现
int quickpow(int a,int b,int c)
{
int ret=1;
while(b)
{
if(b%2==1) ret=ret*a%n;
a=a*a%n;
b=b/2;
}
return ret;
}
二、最大公约数
1.辗转相除法求最大公约数,又称欧几里得算法
int GCD(int x,int y)
{
return y==? x:GCD(y,x%y);
}
2.二进制算法
改进提高GCD的效率,去除因子中的2来降低常数
#include<iostream>
#include<cstdio>
using namespace std;
inline int gcd(int x,int y)
{
int i,j;
if(x==0) return y;
if(y==0) return x;
for(i=0;0==(x&1);++i) x>>=1;
for(j=0;0==(y&1);++j) y>>=1;//去掉所有的2
if(i<j) i=j;
while(1)
{
if(x<y) {int c=x;x=y;y=c;}
if(0==(x-=y)) return y<<i;
while(0==(x&1)) x>>=1;//去掉所有的2
}
}
int main()
{
int m=gcd(25,5);
cout<<m<<endl;
return 0;
}
3.扩展欧几里得算法
在已知的(a,b)时,用扩展欧几里得算法求解一组(p,q),使得p*a+q*b=GCD(a,b)
因为: a*x1+b*y1=gcd(a,b) = gcd(b,a%b)
gcd(b,a%b) =b*x2+(a%b)*y2;
所以:a*x1+b*y1=b*x2+(a%b)*y2 = b*x2 +(a-[a/b]*b)*y2;
=a*y2+b(x2-[a/b]*y2)
即得到: {x1=y2 ; y1= x2-[a/b]*y2}
#include<cstdio>
using namespace std;
int exgcd(int a,int b,int &x,int &y)
{
if(b==0)
{
x=1;
y=0;
return a;
}
int r=exgcd(b,a%b,x,y);
int t=x;
x=y;
y=t-(a/b)*y;
return r;
}
int main()
{
int a,b,x,y,z;
scanf("%d%d",&a,&b);
z=exgcd(a,b,x,y);
printf("%d %d %d\n",z,x,y);
return 0;
}
4.线性同余方程
(1)对于方程a*x+b*y=c 有整数解的必要条件是c%GCD(a,b)=0;
先利用扩展欧几里得算法求出一组 x0,y0,a*x0+b*y0=GCD(a,b);将该式乘以c/GCD(a,b),找到方程的一个解
(2)若GCD(a,b)=1 ,x0,y0为方程的一组解,
方程的额任意一组解可表示为:x=x0+b*t,y=y0+a*t
t=b/GCD(a,b), x=(x%t+t)%t(+-t调整x的正负);
5.最小公倍数
a,b的最大公约数乘以他们的最小公倍数就等于a*b;
最大公约数=(a*b)/gcd(a,b);
6.欧几里得的游戏:
分析两种情况之后:首先拿到A/B>1的人是必胜的,如果A/B一直等于1 就要看有多少局游戏了
三、逆元
1.a,b互质,即GCD(a,b)=1; 可以转化为:a*x+b*y=1再用拓展欧几里得算法求出x。
2.sumdiv 求A^B的所有因子之和
题解链接:
其中:用递归二分求等比数列,用反复平方法计算p^n
四、中国剩余定理
用来求解“模线性方程组”的解
a≡b[1](mod w[1])
a≡b[2](mod w[2])
……
其中W数组中的数互质,求a的值,
解有不同周期的N件事,同在1天内发生,这一天最短什么时候到来
例如题目:Biorhys
假设一年的第N天达到峰值,则下次到达峰值的时间应该为N+T*k(T是周期,k是任意正整数)
三个峰值同时出现的那一天应该满足:
S=N1+T1*k1=N2+T2*k2=N3+t3*k3;
S≡N1(modt1);S≡N2(modT2);S≡N3(modT3);