1. 程式人生 > >hdu 1576 A/B 求逆元模板題

hdu 1576 A/B 求逆元模板題

一些基本概念:

乘法逆元,是指數學領域群G中任意一個元素a,都在G中有唯一的逆元a‘,具有性質a×a'=a'×a=e,其中e為該群的單位元。

用[a]n代表x%n=a.的所有滿足條件的x所組成的集合,其中a為集合中最小非負整數,用最小的非負整數來代表整個集合,即[a]n={a+k*n,k∈Z)

Zn={ [0]n, [1]n ,[2]n ,[3]n ,…, [n-1]n }

在模n乘法群(Zn*,  *)中,Zn*={[a]n∈Zn,且gcd(a,n)=1},單位元為[1]n,不在群裡的元素沒有關於模n的逆元,若某個[a]n有逆元x,那麼[a]n *  [ x ]n===1(mod n)(一個元素與其對應的逆元進行運算結果為單位元),轉換為ax+ny=1,a

n關於整數x,y線性組合,故gcd(a,n)=ax+ny=1.

所以某個元素在模n乘法運算下存在逆元,必須gcd(a,n)=1.

由存在逆元的條件gcd(a,n)=ax+ny=1.可以用exgcd求出x,這裡求出的x可能不是最小非負,要將其轉化為最小非負(代表元素)。另,x有無窮多個解X0=x+k*n,k∈Z,將解得的x化為代表元素,也即a的逆元。

題目思路:B是可以整除A的,且B與9973互質,說明B的模9973乘法運算存在逆元,用X表示B的逆元,那麼(A/B)%9973可以轉化為(n*X)%9973.所以題目要求求出X即可

B*x=1(mod)9973(x為B關於9973的逆元)   => B*x + 9973*y = 1.用擴充套件歐幾里得算出x就是B關於模9973的逆元 (A/B)%9973 = (A*x)%9973

#include<cstdio>
using namespace std;
//求  (A/B)%9973  且gcd(B,9973)=1,B能整除A 
void exgcd(int a,int b,int d,int &x,int &y)
{
	if(!b)
	{
		x=1;y=0;
		d=a;
	}
	else
	{
		exgcd(b,a%b,d,y,x);
		y-=x*(a/b);
	}
}
int main()
{
	int n,b;
	int t;
	scanf("%d",&t);
	while(t--)
	{
		scanf("%d%d",&n,&b);
		int d,x,y;
		exgcd(b,9973,d,x,y);
		x=(x%9973+9973)%9973;//解得B的逆元 
		int ans=(n*x)%9973;
		printf("%d\n",ans);
	}
} 

費馬小定理求逆元:

a^(p-1) ≡1 (mod p)  p是質數,a與p互質

兩邊同除以a

a^(p-2) ≡1/a (mod p) 1/a就是a關於模p的逆元。

逆元x=a^(p-2) (mod p)快速冪解出來就行了

#include<cstdio>
#define ll long long
using namespace std;
int pow(int a,int n,int p)
{
	int ret=1;
	while(n)
	{
		if(n&1)
		ret=((ll)ret*a)%p;
		a=((ll)a*a)%p;
		n>>=1;
	}
	return ret;
}
int main()
{
	int n,b;
	int t;
	scanf("%d",&t);
	while(t--)
	{
		scanf("%d%d",&n,&b);
		b=pow(b,9973-2,9973);
		b=((b%9973)+9973)%9973;
		printf("%d\n",(n*b)%9973);
	}
}