质数、约数、欧拉函数、欧几里得

质数

试除法判定质数

bool isprime(int x)
{
	if(x == 1)
		return false;
	if(x == 2)
		return true;
	for(int i = 2; i <= x/i; i++)
		if(x % i == 0)
			return false;
	return true;
}

分解质因数

vector<pair<int,int>> pf;//Prime Factors
for(int i = 2; i <= x/i; i++)
{
	if(x % i == 0)
	{
		int cnt = 0;
		while(x%i == 0)
		{
			x /= i;
			cnt++;
		}
		pf.push_back({i,cnt});//质因数的值和指数
	}
}
if(x > 1)
	pf.push_back({x,1});

筛质数

vector<int> visit(n,0);
vector<int> prime;
for(int i = 2; i <= n; i++)//n为范围上界
{
	if(visit[i] == 0)
		prime.push_back(i);
	for(int j = 0; j <= n && i*prime[j] <= n; j++)
	{
		visit[i*prime[j]] = 1;
		if(i % prime[j] == 0)
			break;
	}
}

约数

试除法求约数

vector<int> Divisor;
for(int i = 1; i <= x / i; i++)
{
	if(x % i == 0)
	{	
		Divisor.push_back(i);
		if(i != x / i)
			Divisor.push_back(x/i);
	}
}
sort(Divisor.begin(), Divisor.end());//约数从小到大

约数的个数

//约数的个数:(1)质因数分解、(2)求指数乘积(a1 + 1)*(a2+1)*...*(an+1);a指质因数的指数
#include<iostream>
#include <unordered_map>
using namespace std;
const long long mod = 1e9 + 7;
int main()
{
    int n, x;
    cin >> n;
    unordered_map<int,int> hash;
    while(n--)
    {
        cin >> x;
        for(int i = 2; i <= x/i; ++i)
            if(x % i == 0)
                while(x % i == 0)
                {
                    x /= i;
                    hash[i]++;
                }
        if(x > 1) hash[x]++;
    }
    long long ans = 1;
    for(auto i : hash) ans = ans * (i.second + 1) % mod;
    cout << ans;
    return 0;
}

约数的和

//约数的和=(p1^0 + ... + p1^n)*(p2^0 + ... + p2^m)*(pi^0 + ... + pi^j);p表示质因数,n为对应的指数
#include<iostream>
#include<unordered_map>
#include<cmath>
using namespace std;
const long long mod = 1e9 +7;

int main()
{
    unordered_map<int,int> hash;
    int n,x;
    long long res = 1;
    cin >> n;
    while(n--)
    {
        cin >> x;
        for(int i = 2; i <= x/i; i++)
        {
            if(x%i == 0)
                while(x%i==0)
                {
                    x/= i;
                    hash[i]++;
                }
        }
        if(x > 1)
            hash[x]++;
    }
    for(auto p : hash)
    {
        long long a = p.first, b = p.second;
        long long t = 1;
        while (b -- ) t = (t * a + 1) % mod;
        res = res * t % mod;
    }
    cout << res;
    return 0;
}

最大公约数

//辗转相除法
//原理:gcd(a,b) = gcd(b, a mod b) gcd表示最大公约数

#include <iostream>
#include <algorithm>
using namespace std;
int gcd(int a , int b)
{
    return b ? gcd(b , a % b): a;
}
int main()
{
    int n;
    cin >> n;
    while(n--)
    {
        int a,b;
        cin >> a >> b;
        cout << gcd(a,b) << endl;
    }
    return 0;
}

欧拉函数

公式求欧拉函数

![[Pasted image 20230307103540.png]]

//欧拉函数是指一个数n,1~n中与n互质的数的个数
//如Φ(6) = 2;1 2 3 4 5 6中1和5与6互质,则6的欧拉函数等于2
//1.质因数分解 2.容斥原理求欧拉函数
/*步骤:
	1.减去每个质因数的倍数 n/pi
	2.加上两个质因数共同的倍数(在1中减了两次)
	3.减去三个质因数共同的倍数(在2中又多加了一次)
	...
	依次类推减去奇数个质因数的倍数,加上偶数个质因数的倍数
*/
#include<iostream>
using namespace std;
int main()
{
    int n;
    cin >> n;
    while(n--)
    {
        int x; cin >> x;
        long long ans = x;
        for(int i = 2; i <= x /i; i++)
        {
            if(x % i == 0)
            {
                while(x%i == 0)
                    x /= i;
                ans *= (1 - 1.0/i);
            }
        }
        if(x > 1)
            ans *= (1 - 1.0/x);
        cout << ans << endl;
    }
    return 0;
}

筛法求欧拉函数

//质数i的欧拉函数phi[i] = i - 1
//i % prime[j] == 0 ,则i*prime[j]的欧拉函数 = prime[j]*phi[i];pj是i的一个质因子,则i*Prime[j]的全部质因子与i相同,phi[i*prime[j]] = i*prime[j]*(1-1.0/p)...
=prime[j]*phi[i]
//i % prime[j] != 0时,phi[i*prime[j]] = (prime[j] - 1)*phi[i]
//prime[j]不是i的质因子,但是i*prime[j]最大的质因子,i*prime[j]的全部质因子=i的全部质因子+prime[j]
//phi[i*prime[j]] = (pj - 1) *phi[i]
#include<iostream>
#define N 1000010

using namespace std;
typedef long long ll;

int cnt;
int phi[N];
int prime[N];
bool st[N];
ll get_eulers(int x)
{
    
    phi[1] = 1;
    for(int i = 2; i <= x; i++)
    {
        if(!st[i])
        {
            prime[cnt++] = i;
            phi[i] = i - 1;
        }
        for(int j = 0; prime[j] <= x / i; j++)
        {
            st[i*prime[j]] = true;
            if(i % prime[j] == 0)
            {
                phi[i*prime[j]] = prime[j] * phi[i];
                break;
            }
            phi[i*prime[j]] = (prime[j] - 1) * phi[i];
        }
    }
    ll ans = 0;
    for(int i = 1; i <= x; i++)
        ans += phi[i];
    return ans;
}
int main()
{
    int n;
    cin >> n;
    cout << get_eulers(n) <<endl;
    return 0;
} 

快速幂

快速幂

求s的t次方
long long ans = 1;
long long x = s;
while(t)
{
	if(t % 2 == 1)
		ans *= x;
	x *= x;
	t /= 2;
}

快速幂求逆元

#include <bits/stdc++.h>
using namespace std;

// a^b mod p
// 所谓快速幂的快速在于将指数b分解成了二进制数表示,b= xk ... x1 x0,而代码实现的核心则是反复平方
// a^b = a^(2^xk+...2^x1+2^x0) = a^(2^xk) * ... * a^(2^x1) * a^(2^x0),这样就把a^b分解成logb个数相乘了,其中相邻两项中后一项是前一项的平方
int power(int a,int b,int p)
{
    int res=1%p;
    while(b)
    {
        // 开始取b二进制数的末尾,若为1,则res需要乘上这一项
        // 这里首先判断b的二进制末尾是否为1,是因为a的指数幂是从a开始的
        if(b&1)res=1LL*res*a%p;
        // 然后移除二进制数的最后一位数字,方便下次枚举
        b>>=1;
        // 然后将当前a的指数幂进行平方,得到下一项的a的指数幂
        a=1LL*a*a%p;
    }
    return res;
}

int main()
{
    int n;scanf("%d",&n);
    while(n--)
    {
        // 逆元公式推导:a/b ≡ a*x (mod m);等式两边同时乘 b,得:b*a/b ≡ a*b*x (mod m),化简得:a ≡ a*b*x (mod m)
        // 所以 b*x ≡ 1 (mod m),由费马小定理,b^(m-1) ≡ 1 (mod m) 其中 m 为质数。联系本方程 b * b^(m-2) ≡ 1,所以 b 的逆元 x 为 b^(m-2);
        // 若 b 是 m 的倍数,则无解的,因为 b 是 m 的倍数,那么 b*x 也必定是 m 的倍数,模 m 的余数为 0,必定不为 1,是无解的情况。
        // 若 b 不是 m 的倍数,由于 m 是质数,那么 b 与 m 是互质的,由费马小定理可知,b^(m-1) ≡ 1 (mod m),一定存在逆元,一定有解。
        int b,m;scanf("%d%d",&b,&m);
        // b 是 m 的倍数时,是无解的
        if(b%m==0)puts("impossible");
        // 否则 b 的逆元是 b^(m-2)%m
        else printf("%d\n",power(b,m-2,m));
    }
    return 0;
}

扩展欧几里得算法

拓展欧几里得算法

裴蜀定理:对于任意正整数a、b,一定存在非零整数x、y,使得ax+by = k*gcd(a,b),特别地一定存在整数x,y使k == 1

(a,b) == (b, a\mod\ b) == (b, a - [a/b] * b)

ax + by == (a,b)

两式相等,得:
ax + by == bm + (a-[a/b]b)n
化简有:
ax + by == an + b(m - [a/b]
n)
即:
$x = n 且 y = m - [a/b]*n

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

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

int main()
{
    int n;cin >> n;
    while(n--)
    {
        int a,b;
        scanf("%d%d",&a,&b);
        int x,y;
        exgcd(a, b, x, y);
        printf("%d %d\n", x, y);
    }
    return 0;
}

线性同余方程

  • 因为ax≡b(mod m)等价于 ax-b是m的倍数,因此线性同余方程等价为ax+my = b
  • 根据 Bezout 定理,上述等式有解当且仅当 gcd(a,m)|b
  • 因此先用扩展欧几里得算法求出一组整数x_0,y_0使得ax_0+my_0 = gcd(a,m),然后x=x_0*b/gcd(a,m)%m即是所求
#include<iostream>
#include<cstdio>
using namespace std;

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

int main()
{
    int n;cin >> n;
    while(n--)
    {
        int a,b,m;
        scanf("%d%d%d",&a,&b,&m);
        int x,y;
        int d = exgcd(a, m, x, y);
        if(b % d == 0)
        {
            x = (long long)x*b/d%m;
            cout << x << endl;
        }
        else
            cout << "impossible" << endl;
    }
    return 0;
}
posted @ 2023-03-12 14:39  瞻鹤  阅读(73)  评论(0编辑  收藏  举报