数学知识——欧拉函数、快速幂、扩展欧几里得算法

欧拉函数
  • 欧拉函数定义为 ϕ ( n ) = 1 − n 中 与 n 互 质 的 个 数 \phi(n)=1-n中与n互质的个数 ϕ(n)=1nn,互质就是最大公约数是1
  • 欧拉函数求解公式:
    将n分解质因数: n = p 1 a 1 + p 2 a 2 + . . . + p k a k n=p_1^{a1}+p_2^{a2}+...+p_k^{ak} n=p1a1+p2a2+...+pkak,
    ϕ ( n ) = n ∗ ( 1 − 1 p 1 ) ∗ ( 1 − 1 p 2 ) ∗ . . . . . ∗ ( 1 − 1 p k ) \phi(n)=n*(1-\frac{1}{p_1})*(1-\frac{1}{p_2})*.....*(1-\frac{1}{p_k}) ϕ(n)=n(1p11)(1p21).....(1pk1)
  • 证明:略
    模板题代码
#include
using namespace std;

int main()
{
    int n,a;
    scanf("%d",&n);
    while(n--){
        scanf("%d",&a);
        int res = a;
        for(int i=2;i<=a/i;i++){
            if(a%i==0){
                res = res/i*(i-1);//防止产生小数
                while(a%i==0)a /= i;
            }
        }
        if(a>1) res = res/a*(a-1);
        printf("%d\n",res);
    }
    return 0;
}
筛法求欧拉函数

筛法求欧拉函数,是在质数的线性筛法基础上进行改进的,
代码:

#include
using namespace std;
const int N = 1e6+10;
int primes[N],cnt,phi[N];
bool st[N];

long long get_oula(int n){
    phi[1] = 1;
    for(int i=2;i<=n;i++){
    //当i为质数时,1到i-1都与i互质
        if(!st[i])primes[cnt++] = i,phi[i] = i-1;
        for(int j=0;primes[j]<=n/i;j++){
            st[primes[j]*i] = true;
            if(i%primes[j]==0){
            	/*******
            	当i%primes[j]==0时,primes[j]是i的最小质因数,
            	primes[j]*i与i的质因数相同,因此,欧拉函数仅仅多乘了
            	primes[j]
            	********/
                phi[primes[j]*i] = phi[i]*primes[j];
                break;
            }
            /******
            当i%primes[j]!=0时,primes[j]是primes[j]的最小质因数,但
            不是i的质因数,而primes[j]*i的其他质因数与i相同,因此
            primes[j]*i的欧拉函数比phi[i]多乘了primes[j]*(1-1/primes[j])
            ******/
            phi[primes[j]*i] = phi[i]*(primes[j]-1);
        }
    }
    long long res = 0;
    for(int i=1;i<=n;i++)res += phi[i];
    return res;
}

int main()
{
    int n;
    scanf("%d",&n);
    long long res = get_oula(n);
    printf("%lld",res);
    return 0;
}

欧拉定理如果a与n互质,则 a ϕ ( n ) = 1 ( m o d   n ) a^{\phi(n)}=1(mod \ n) aϕ(n)=1(mod n)
如果p为质数,则 a p − 1 = 1 ( m o d   p ) a^{p-1}=1(mod\ p) ap1=1(mod p).

快速幂

快速幂可以在O(logk)的时间复杂度内求出 a k m o d   p a^k mod \ p akmod p
快速幂的步骤:

  • 预处理 a 2 0 m o d   p 、 a 2 1 m o d   p . . . . . . . . . a 2 l o g k m o d   p a^{2^0}mod\ p、a^{2^1}mod \ p.........a^{2^{logk}}mod\ p a20mod pa21mod p.........a2logkmod p
  • 将k按二进制展开,取是1的位,进行处理。
#include
using namespace std;
typedef long long LL;
LL a,b,p,n;//防止溢出,全部用long long

int main()
{
    scanf("%lld",&n);
    while(n--){
        scanf("%lld%lld%lld",&a,&b,&p);
        LL res = 1;
        while(b){//一边乘,一边预处理,用数组预处理好像不太对,我太菜了不会写。
            if(b&1)res = res*a%p;
            b = b>>1;
            a = a*a%p;
        }
        printf("%lld\n",res);
    }
    return 0;
}
快速幂求逆元

逆元定义:若整数 b,m 互质,并且对于任意的整数 a,如果满足 b|a,则存在一个整数 x,使得 a/b≡a×x(modm),则称 x 为 b 的模 m 乘法逆元,记为 b − 1 ( m o d   m ) b^{−1}(mod\ m) b1(mod m)
b 存在乘法逆元的充要条件是 b 与模数 m 互质。当模数 m 为质数时, b m − 2 b^{m−2} bm2 即为 b 的乘法逆元。
逆元的作用就是将除法转换成乘法。

#include
using namespace std;
typedef long long LL;
LL n,a,p;

LL qmi(LL a,LL b,LL p){
    LL res = 1;
    while(b){
       if(b&1)res = res*a%p;
       b = b>>1;
       a = a*a%p;
    }
    return res;
}

int main()
{
    scanf("%lld",&n);
    while(n--){
        scanf("%lld%lld",&a,&p);
        if(a%p==0)printf("impossible\n");
        else{
            LL res = qmi(a,p-2,p);
            printf("%lld\n",res);
        }
    }
    return 0;
}
扩展欧几里得算法

裴蜀定理若a,b是整数,且gcd(a,b)=d,那么对于任意的整数x,y,ax+by=m中的m一定是d的倍数。(特别地,如果a、b是整数,那么一定存在整数x、y使得ax+by=gcd(a,b)。)
在欧几里得算法中:

int gcd(int a,int b){
	if(!b){
		return a;
	}
	return gcd(b,a%b);
}

就是说当b=0时,a中存的就是最大公约数,则当b=0时,一定有: a × 1 + b × 0 = g c d ( a , b ) a×1+b×0=gcd(a,b) a×1+b×0=gcd(a,b),此时x = 1,y = 0;
而当b!=0时:

假设之前有 a × x 1 + b × y 1 = g c d a×x_1+b×y_1=gcd a×x1+b×y1=gcd
下一步计算gcd(b,a%b)时有 b × x 2 + ( a % b ) × y 2 = g c d b×x_2+(a\%b)×y_2=gcd b×x2+(a%b)×y2=gcd
所以, a × x 1 + b × y 1 = b × x 2 + ( a % b ) × y 2 a×x_1+b×y_1 = b×x_2+(a\%b)×y_2 a×x1+b×y1=b×x2+a%b)×y2,a%b = a-(a/b)*b;
a x 1 + b y 1 = b x 2 + a y 2 − ( a / b ) ∗ b y 2 ax_1+by_1=bx_2+ay_2-(a/b)*by_2 ax1+by1=bx2+ay2(a/b)by2
所以, x 1 = y 2 , y 1 = x 2 − ( a / b ) ∗ y 2 x_1=y_2,y1=x2-(a/b)*y_2 x1=y2,y1=x2(a/b)y2

扩展欧几里得算法如下:

int exgcd(int a,int b,int& x,int& y){
	if(!b){
		x = 1;
		y = 1;
		return a;
	}
	int d = exgcd(b,a%b,x,y);
	int temp = x;
	x = y;
	y = temp - (a/b)*y;
	return d;
}
线性同余方程

给定 n 组数据 a i , b i , m i a_i,b_i,m_i ai,bi,mi,对于每组数求出一个 x i x_i xi,使其满足 a i × x i ≡ b i ( m o d   m i ) a_i×x_i≡b_i(mod\ m_i) ai×xibi(mod mi)

也就是 a x + m y = b ax+my=b ax+my=b,可以用扩展欧几里得算法求x和y,只有b是gcd(a,m)的倍数时有解,最后x扩大(b/d)就是解。

#include
using namespace std;
int a,b,m;
int n;

int exgcd(int a,int b,int& x,int &y){
    if(!b){
        x = 1;
        y = 0;
        return a;
    }
    int d = exgcd(b,a%b,x,y);
    int temp = x;
    x = y;
    y = temp - (a/b)*y;
    return d;
}

int main()
{
    scanf("%d",&n);
    while(n--){
        scanf("%d%d%d",&a,&b,&m);
        int x,y;
        int d = exgcd(a,m,x,y);
        if(b%d)printf("impossible\n");
        else printf("%lld\n",(long long)x*(b/d)%m);
    }
    return 0;
}

你可能感兴趣的:(acwing算法基础课学习笔记)