HDU - 1576 乘法逆元(扩展欧几里得算法) 难度:算法入门 复杂度:有点复杂 (待完善)...

HDU - 1576 乘法逆元(扩展欧几里得算法) 难度:算法入门 复杂度:有点复杂 (待完善)..._第1张图片

先简介下扩展欧几里得算法:

据说可以证明方程ax+by=gcd(a,b)必然有解,而且不止一组解(gcd指最大公约数)

朴素的欧几里得算法就是辗转相除法,用来求gcd的

因为ff67cf717f8688852acd41fe14a284f74ba.jpg

最后会有一方等于0,就能求出gcd(a,b),这就是辗转相除法

 

扩展欧几里得算法可以解出方程ax+by=gcd(a,b)的一组解,假设a>b>=0
以下是一段简单的推导:

HDU - 1576 乘法逆元(扩展欧几里得算法) 难度:算法入门 复杂度:有点复杂 (待完善)..._第2张图片

因此,只要知道x2,y2就能知道x1,y1,而x2,y2也能通过x3,y3得知,这样往下递推到b=0为止

回到题目中,设X=A/B,

d3f23bb0c379144e530445bc61a3c690bfc.jpg

x*B+9973*y=gcd(B,9973)=1,用扩展欧几里得算法可以算出这个方程的一组解,即x和y可知
方程两边乘以n得x*n*B+y*n*9973 = n = XB+A/9973*9973
对比式子两边可知x*n=X
那么知道了x就知道X了,X就是A/B
X%9973(即x*n%9973)就是题目要的答案

但问题又来了,扩展欧几里得算法算出的x是负数怎么办?
负数取模再加一份模得到正数,然后输出即可,即x*n%9973+9973
(由于本人暂时也不太明白为何负数取模要想得到正数结果在这题当中可以这样转化,加上时间紧迫,只能回头再做补充了,至少代码能A)

HDU - 1576 乘法逆元(扩展欧几里得算法) 难度:算法入门 复杂度:有点复杂 (待完善)..._第3张图片

#include
using namespace std;
int exGcd(int a, int b, long long &x, long long &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 T;
	cin >> T;
	long long n, B;
	long long x, y;
	for (T; T > 0; T--)
	{
		cin >> n >> B;
		exGcd(B, 9973, x, y);
		cout << x*n % 9973 + 9973 << endl;
	}
	return 0;
}

 

转载于:https://my.oschina.net/u/4035395/blog/3011482

你可能感兴趣的:(HDU - 1576 乘法逆元(扩展欧几里得算法) 难度:算法入门 复杂度:有点复杂 (待完善)...)