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乘法運算下存在逆元,必須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);
}
}