质数、约数、欧拉函数、欧几里得
质数
试除法判定质数
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;
}