A/B Problem
时间限制:1000 ms | 内存限制:65535 KB
难度:3
输入
数据的第一行是一个T,表示有T组数据.
每组数据有两个数n(0 <= n < 9973)和B(1 <= B <= 10^9).
输出
对应每组数据输出(A / B) % 9973.
样例输入
2 1000 53 87 123456789
样例输出
7922 6060
描述
已知:
1. n = (A % 9973);
2. gcd(B, 9973) = 1;
计算:
(A / B) % 9973
这题是裸逆元题,将B的逆元B’求出来即可. BB'=1%9973=>(BB')%9973=1(1的前提条件) (A/B)%9973=(A/B)*1%9973=>(A/B)*(BB')%9973%9973=>(AB')%9973%9973(两个模可去掉一个)=>(n*B')%9973就是答案了
逆元的定义:假设x是a的逆元,那么 a * x = 1(mod p)(这里p=9973,以下都是)
求逆元以下有两种方法,一个是费马小定理求逆元,一个是扩展欧几里得求逆元
注意的是 ( a/b )%p = (a%p / b%p) % p 这个公式是不成立的;;;;
先说费马小定理
公式:a^(p-1)=1%p(与a * x = 1(mod p) 是不是很像); 化简为 a*a^(p-2)=1%p;a^ (p-2)就是a的逆元,所以上题调用幂pow(B,9973-2)就是B'了;(也可快速幂)
后说扩展欧几里得
大概就是Ax+By=1; 逆元的定义 a * x = 1(mod p) 可化简为 a*x=p*y+1=>a*x-p*y=1 与欧几里得式子很像
费马小定理 (我这里是快速幂模板,从杜神代码那复制过来的)
#include<bits/stdc++.h>
#define in(a) scanf("%d",&a)
#define ind(a) scanf("%lld",&a)
using namespace std;
typedef long long ll;
const int mod=9973;
ll gcd(ll a,ll b) { return b?gcd(b,a%b):a;}
ll powmod(ll a,ll b) {ll res=1;a%=mod; assert(b>=0); for(;b;b>>=1){if(b&1)res=res*a%mod;a=a*a%mod;}return res;}
ll inv(ll a)
{
return powmod(a,mod-2);
}
int main()
{
int t;
in(t);
while(t--)
{
ll n,b;
ind(n);ind(b);
ll sum=inv(b);
printf("%lld\n",(n*sum)%mod);
}
}
欧几里得
#include <iostream>
#include<cstdio>
using namespace std;
const int MOD = 9973;
/*
* 扩展欧几里得法(求ax + by = gcd)
*/
// 返回d = gcd(a, b);和对应于等式ax + by = d中的x、y
long long extendGcd(long long a, long long b, long long &x, long long &y)
{
if (a == 0 && b == 0)
{
return -1; // 无最大公约数
}
if (b == 0)
{
x = 1;
y = 0;
return a;
}
long long d = extendGcd(b, a % b, y, x);
y -= a / b * x;
return d;
}
// 求逆元 ax = 1(mod n)
long long modReverse(long long a, long long n)
{
long long x, y;
long long d = extendGcd(a, n, x, y);
if (d == 1)
{
return (x % n + n) % n;
}
else
{
return -1; // 无逆元
}
}
int main(int argc, const char * argv[])
{
int T;
cin >> T;
long long n, B;
while (T--)
{
cin >> n >> B;
long long b = modReverse(B, MOD);
//printf("逆元:%lld\n",b);
cout << (n * b) % MOD << '\n';
}
return 0;
}