gcd(a, b) = gcd(b, a % b)
两个正整数a和b(a>b),它们的最大公约数等于a除以b的余数c和b之间的最大公约数
a可以表示成a = kb + r,则r = a % b;
int gcd(int a, int b) { //递归形式
if(b == 0) return a; //当 b == 0 时,(a,0)的最大公约数是a
return gcd(b, a % b);
}
int gcd(int a, int b) { //非递归形式
int tmp = a;
while(b) {
a = b;
b = tmp % b;
}
return a;
}
复杂度:O( log(max(a, b)) )
用来在已知a, b求解一组x,y使得 a * x + b * y = Gcd(a, b),扩展欧几里德常用在求解横线性方程及方程组中。
//if(a < 0) a = -a, c = -c;
int exgcd(int a, int b, int &x, int &y) { //a必须大于0
if(b == 0) {
x = 1, y = 0;
return a;
}
int d = exgcd(b, a % b, y, x);
y -= (a / b) * x;
return d;
}
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2669
The Sky is Sprite.
The Birds is Fly in the Sky.
The Wind is Wonderful.
Blew Throw the Trees
Trees are Shaking, Leaves are Falling.
Lovers Walk passing, and so are You.
…………………………..Write in English class by yifenfei
Girls are clever and bright. In HDU every girl like math. Every girl like to solve math problem!
Now tell you two nonnegative integer a and b. Find the nonnegative integer X and integer Y to satisfy X*a + Y*b = 1. If no such answer print “sorry” instead.
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
typedef long long LL;
LL exgcd(LL a, LL b, LL &x, LL &y) {
if(b == 0) {
x = 1, y = 0;
return a;
}
LL d = exgcd(b, a % b, y, x);
y -= (a / b) * x;
return d;
}
int main()
{
LL a, b;
while(cin >> a >> b) {
LL x, y;
LL gcd = exgcd(a, b, x, y);
if(1 % gcd) cout << "sorry" << endl;
else { //由于gcd == c == 1所以直接对方程ax + by = gcd(a,b)求解即可
while(x < 0) { //题目要求x必须大于0
x += b / gcd;
y -= a / gcd;
}
cout << x << " " << y << endl;
}
}
return 0;
}
同余方程 ax ≡ b (mod n) 对于未知数 x 有解,当且仅当 gcd(a,n) | b。且方程有解时,方程有 gcd(a, n) 个解,求解方程 ax ≡ b (mod n) 相当于求解方程 ax+ ny = b(x, y为整数)。
每个解之间的间隔dx = n / d。
题目链接:http://poj.org/problem?id=1061
两只青蛙在网上相识了,它们聊得很开心,于是觉得很有必要见一面。它们很高兴地发现它们住在同一条纬度线上,于是它们约定各自朝西跳,直到碰面为止。可是它们出发之前忘记了一件很重要的事情,既没有问清楚对方的特征,也没有约定见面的具体位置。不过青蛙们都是很乐观的,它们觉得只要一直朝着某个方向跳下去,总能碰到对方的。但是除非这两只青蛙在同一时间跳到同一点上,不然是永远都不可能碰面的。为了帮助这两只乐观的青蛙,你被要求写一个程序来判断这两只青蛙是否能够碰面,会在什么时候碰面。
我们把这两只青蛙分别叫做青蛙A和青蛙B,并且规定纬度线上东经0度处为原点,由东往西为正方向,单位长度1米,这样我们就得到了一条首尾相接的数轴。设青蛙A的出发点坐标是x,青蛙B的出发点坐标是y。青蛙A一次能跳m米,青蛙B一次能跳n米,两只青蛙跳一次所花费的时间相同。纬度线总长L米。现在要你求出它们跳了几次以后才会碰面。
求余方程问题
假设青蛙跳了t次,根据题意列出方程:(x + t * m) % L = (y + t * n) % L ,转化为(m - n) * t + k * L = y - x,那么现在已经符合ax + by = c方程了,设a = m-n,b = L,c = y-x,由于ax + by = gcd(a, b)而不是c,因此只有当c | gcd(a,b)时才有解。
此时方程两边同时乘c / gcd(a,b),就得到 c / gcd(a, b) * (ax + by) = c 方程了。
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
typedef pair PLL;
typedef vector vec;
typedef vector mat;
const int MaxN = 2e3 + 5;
const int MaxM = 1e5 + 5;
const int Mod = 1e9 + 7;
const int INF = 0x3f3f3f3f;
LL exgcd(LL a, LL b, LL &x, LL &y) {
if(b == 0) {
x = 1, y = 0;
return a;
}
LL d = exgcd(b, a % b, y, x);
y -= (a / b) * x;
return d;
}
int main()
{
LL x, y, m, n, l;
scanf("%lld %lld %lld %lld %lld", &x, &y, &m, &n, &l);
LL a = m - n, b = l, c = y - x;
if(a < 0) a = -a, c = -c;
LL gcd = exgcd(a, b, x, y);
if(c % gcd) printf("Impossible\n");
else {
x = x * c / gcd;
int base = b / gcd;
while(x < 0) x += base; //x必须是正数
printf("%lld\n", x % base); //保证x最小
}
return 0;
}
对于正整数a,如果有 ax ≡ 1 (mod n),那么把这个同余方程中的最小正整数解x叫做a模x的逆元。
对于同余方程 ax ≡ 1(mod n), gcd(a, n) = 1 的求解就是求解方程ax + ny = 1。
费马小定理:(m为素数)
推导过程:
故 为a在模m下的逆元(用快速幂求即可)。但有限制条件:m必须为质数,且a,m互质。
将a,m代入扩展gcd公式ax + by = gcd(a, b) 得出公式ax + my = gcd(a, m),令a,m互质,则gcd(a, m) = 1,得出ax +my = 1,即,此时x就是a的逆元。