hdu 1576 A/B p1082 同余方程 逆元的几种求法(扩展欧几里得,费马小定理或欧拉定理,特例,打表等)

版权声明:点个关注(^-^)V https://blog.csdn.net/weixin_41793113/article/details/89047859

模运算
几个常用的定律:

( a + b ) mod p = ( a mod p + b mod p ) mod p

( a * b ) mod p = ( (a mod p) * (b mod p) ) mod p

c * ( a mod p ) = ( c *a ) mod ( c *b )
 

扩展欧几里得

void Exgcd(ll a, ll b, ll &x, ll &y) {
    if (!b) x = 1, y = 0;
    else Exgcd(b, a % b, y, x), y -= a / b * x;
}
int main() {
    ll x, y;
    Exgcd (a, p, x, y);
    x = (x % p + p) % p;
    printf ("%d\n", x); //x是a在mod p下的逆元
}

快速幂

这个做法要利用 费马小定理

若 p 为素数, a 为正整数,且 a 、 p 互质。 则有 a^{p-1} \equiv 1 (\bmod {p}) 。

这个我们就可以发现它这个式子右边刚好为 11 。

所以我们就可以放入原式,就可以得到:

a∗x≡1(mod p)

a*x\equiv a^{p-1} \pmod pa∗x≡a p−1 (modp)

x \equiv a^{p-2} \pmod px≡a p−2 (modp)
所以我们可以用快速幂来算出a^{p-2} \pmod p的值,这个数就是它的逆元了

代码也很简单:

ll fpm(ll x, ll power, ll mod) {//快速幂求乘法逆元,谨记,p是一个素数
    x %= mod;
    ll ans = 1;
    for (; power; power >>= 1, (x *= x) %= mod)
        if(power & 1) (ans *= x) %= mod;
    return ans;
}
int main() {
    ll x = fpm(a, p - 2, p); //x为a在mod p意义下的逆元
}

线性算法

这里有一个模板题目,就是洛谷的 P3811 【模板】乘法逆元 
题目传送门:https://www.luogu.org/problemnew/show/P3811

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
long long x,y,n,f[3000010];
void work(long long n,long long p)//线性求逆元,时间复杂度O(n) 
{
    f[1]=1;
    for(long long i=2;i<=n;i++)
    {
        f[i]=-(p/i)*f[p%i];
        f[i]=(f[i]%p+p)%p;
    }
}
int main()
{
    long long a,p,b,n,i;
    cin>>n>>p;
    work(n,p); 
    for(long long i=1;i<=n;i++)
    {
        printf("%lld\n",f[i]);//处理出   最小正整数!!
    }

    return 0;
}

扩展欧几里得模板 

#define ll long long int
ll a,b,x,y;

void exgcd(ll a,ll b,ll &x,ll &y){//扩展欧几里得模板
    if(!b)//b==0
        x=1,y=0;
    else
        exgcd(b,a%b,y,x),y-=a/b*x;
}
long long x,y,n;//最好定全局变量

void exgcd(long long a,long long b)//也适合javaer
{
    if(b==0) //当b=0时就是遇到了特解,可以递归回去算答案了
    {
        x=1,y=0;
        return ;
    }
    exgcd(b,a%b);
    long long k;
    k=x;
    x=y;
    y=k-(a/b)*y;
}

P1082 同余方程 

题目描述

求关于x的同余方程 a x \equiv 1 \pmod {b}ax≡1(modb) 的最小正整数解。

输入输出格式

输入格式:

一行,包含两个正整数 a,ba,b,用一个空格隔开。

输出格式:

一个正整数 x0,即最小正整数解。输入数据保证一定有解。

输入输出样例

输入样例#1: 复制

3 10

输出样例#1: 复制

7

说明

【数据范围】

对于 40%的数据,2 ≤b≤ 1,000;

对于 60%的数据,2 ≤b≤ 50,000,000;

对于 100%的数据,2 ≤a, b≤ 2,000,000,000。

NOIP 2012 提高组 第二天 第一题

扩展欧几里得模板

#include<iostream>
#include<cstdio>
using namespace std;
#define ll long long int
ll a,b,x,y;

void exgcd(ll a,ll b,ll &x,ll &y){//扩展欧几里得模板
    if(!b)//b==0
        x=1,y=0;
    else
        exgcd(b,a%b,y,x),y-=a/b*x;
}


int main(){
    ios::sync_with_stdio(false);
    while(~scanf("%d%d",&a,&b)){
        exgcd(a,b,x,y);
        cout<<(x%b+b)%b;
    }

	return 0;
}

欧拉函数+快速幂写法 复杂度O(logn+sqrt(n)) 好处 ax=1(mod b) 不需要a,b互

#include<iostream>
#include<cstdio>
using namespace std;

#define ll long long int

ll Euler(ll n){//O(sqrt(n))
    ll ans = n;
    for(int i=2;i*i<=n;i++)
        if(n%i==0){
            ans = ans/i*(i-1);
            while(n%i==0)
                n/=i;
        }
    if(n>1)
        ans = ans/n*(n-1);
    return ans;
}

ll mod_pow(ll a,ll n,ll mod){
    ll ans = 1;
    while(n>0){
        if(n%2==1)
            ans = ans*a%mod;
        a = a*a%mod;
        n>>=1;
    }
    return ans;
}

int main(){//ax=1(mod b)
    ll a,b;
    while(~scanf("%lld%lld",&a,&b)){
        printf("%lld\n",mod_pow(a,Euler(b)-1,b));
    }

	return 0;
}

kuangbin模板——扩展欧几里得

//返回d=gcd(a,b);和对应于等式ax+by=d中的x,y
long long extend_gcd(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=extend_gcd(b,a%b,y,x);
    y-=a/b*x;
    return d;
}
//*********求逆元素*******************
//ax = 1(mod n)
long long mod_reverse(long long a,long long n)
{
    long long x,y;
    long long d=extend_gcd(a,n,x,y);
    if(d==1) return (x%n+n)%n;
    else return -1;
}

hdu1576 A/B

Problem Description

要求(A/B)%9973,但由于A很大,我们只给出n(n=A%9973)(我们给定的A必能被B整除,且gcd(B,9973) = 1)。

Input

数据的第一行是一个T,表示有T组数据。
每组数据有两个数n(0 <= n < 9973)和B(1 <= B <= 10^9)。

Output

对应每组数据输出(A/B)%9973。

Sample Input

2

1000 53

87 123456789

Sample Output

7922

6060

扩展欧几里得

#include<iostream>
#include<cstdio>
using namespace std;

#define MOD 9973
#define ll long long int

ll exgcd(ll a,ll b,ll &x,ll &y){
    if(a==0 && b==0)
        return -1;//无最大公约数
    if(b==0){
        x=1;
        y=0;
        return a;
    }
    ll d = exgcd(b,a%b,y,x);
    y-=a/b*x;
    return d;
}

//ax=1(mod n)
ll mod_reverse(ll a,ll n){
    ll x,y;
    ll d = exgcd(a,n,x,y);
    if(d==1)
        return (x%n+n)%n;
    else
        return -1;
}


int main(){
    ll n,b;
    int T;
    scanf("%d",&T);
    while(T--){
        scanf("%lld%lld",&n,&b);
        ll x = mod_reverse(b,MOD);
        printf("%lld\n",n*x%MOD);
    }

	return 0;
}

费马小定理+快速幂

#include<iostream>
#include<cstdio>
using namespace std;

#define ll long long int
#define MOD 9973
ll mod_pow(ll a,ll n,ll mod){
    ll ans = 1;
    while(n>0){
        if(n%2==1)
            ans = ans*a%mod;
        a = a*a%mod;
        n>>=1;//右移
    }
    return ans;
}


int main(){
    int T;
    ll n,b;
    scanf("%d",&T);
    while(T--){
        scanf("%lld%lld",&n,&b);
        b = mod_pow(b,MOD-2,MOD);
        b = (b%MOD+MOD)%MOD;
        printf("%lld\n",n*b%MOD);
    }

	return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_41793113/article/details/89047859