数论基础
欧拉函数
定义
欧拉函数 \(\phi(n)\) 代表的是 \([1,n]\) 之间与 \(n\) 互质的数量。
公式
\(\phi(n) = n \times (1- \dfrac{1}{p_1})\times (1- \dfrac{1}{p_2})\times (1- \dfrac{1}{p_3}) \times …… \times (1- \dfrac{1}{p_k})\)
其中:\(n\) 有 \(k\) 个质因数,而 \(p_i\) 就是其中的一个质因数。
推导
如何推导
推导的方式要用到容斥原理。
欧拉函数 \(\phi(n)\) 代表的是 \([1,n]\) 之间与 \(n\) 互质的数量,但是我们发现比较难想,于是正难则反,直接从不互质来入手,然后减掉就得出结果了。
如果 \(i\) 是不互质,那么 \(i\) 和 \(n\) 必将有共同的质因数。所以,我们可以通过 \(n\) 的每一个质因数进行倍数处理,然后容斥算出有多少是不互质的,因此也就可得出答案了。
推导过程
我们设:\(n\) 的质因数是:{ \(p_1,p_2,……,p_k\) }。
那么 \(p_i\) 在 \([1,n]\) 这个区间中的倍数就有 \(\dfrac{n}{p_i}\)。
为了方便推导,我们暂且先设 \(k=3\)。
于是就可以得到公式:
- \(n-\dfrac{n}{p_1}-\dfrac{n}{p_2}-\dfrac{n}{p_3}+\dfrac{n}{p_1 \times p_2}+\dfrac{n}{p_1 \times p_3} + \dfrac{n}{p_2 \times p_3} -\dfrac{n}{p_1 \times p_2 \times p_3}\)
在进行通分之后,我们发现这个式子其实已经等于 \(\phi(n)\) 了。
得证。
代码
定义法
-
只能处理一个数的 \(\phi\)。
-
思路是一边分解质因数一遍处理欧拉函数
int get_euler(int n) { int euler = n; for (int i = 2; i <= n / i; i++) { if (n % i == 0) { euler = euler / i * (i - 1) //euler = euler * (1 - 1 / i); while (n % i == 0) n /= i; } } if (n > 1) euler = euler / n * (n - 1); return euler; }
线性筛法
-
在进行筛法的时候处理欧拉函数
-
可以在一次线性筛中处理出 \([1,n]\) 中的欧拉函数
int primes[N], cnt = 0; int euler[N]; bool st[N]; int get_euler(int n) { euler[1] = 1; // 1的欧拉函数值是1 // 线性筛模板 + 过程中求欧拉函数 for (int i = 2; i <= n; i++) { if (!st[i]) { primes[cnt ++] = i; euler[i] = i - 1; // (1)式 } for (int j = 0; primes[j] <= n / i; j++) { st[i *primes[j]] = true; if (i % primes[j] == 0) { euler[i * primes[j]] = euler[i] * primes[j]; //(2)式 break; } euler[i * primes[j]] = euler[i] * (primes[j] - 1);//(3)式 } } // 最后,euler数组中存的就是 1 ~ n 每个数的欧拉函数值 } urn euler; }
一些等式
-
当 \(n\) 是质数的时候,\(\phi(n) = n-1\)。
-
当 \(n\) 是质数的时候,存在 \(n^k\) 使得 \(\phi(n^k) = (n-1) \times n ^{k-1}\)
-
积性函数,如果 \(n\) 与 \(m\) 互质,则 \(\phi(n×m)=\phi(n)×\phi(m)\)
参考文献:
费马小定理
如果 \(p\) 是一个质数,\(a\) 不是 \(p\) 的倍数。那么必然存在: \(a^{p-1}\equiv{1}\pmod{p}\)
谁想推啊!所以不推了。(~逃
逆元
逆元的定义
如果一个线性同余方程,\(p\) 为质数,\(a\) 与 \(p\) 互质,那么 \(ax \equiv{1} \pmod{p}\),\(x\) 就是 \(a\) 与 \(p\) 的逆元。
快速幂求逆元
单个求,时间复杂度 \(\log n\) 。适合单个求。
推导
\(\because\) 根据费马小定理我们可以知道:\(a^{p-1}\equiv{1}\pmod{p}\)
\(\therefore\) \(a \times a^{p-2}\equiv{1}\pmod{p}\)
所以什么?很容易看出来 \(x = a^{p-2}\)。
注意了,我们是由费马小定理推出 \(x\) 的值的,但是我们不能根据这个式子去推导费马小定理。
代码
记得 #define int long long~
inline ll ksm(int a,int b){
ll ans = 1;
while(b){
if(b & 1) ans = mod(ans * a,p);
b >>= 1,a = mod(a * a,p);
}return ans;
}
inline void solve(){//快速幂求逆元
cin >> n >> p;
for(re int i(1);i <= n;++i){
println(mod(ksm(i,p-2),p));
}
}
线性求逆元
整体的思想就是利用已知的数据从前往后推,以达到 \(O(n)\) 复杂度的需求,适合求多组数据的题目。
首先,我们有一个 \(1^{-1}\equiv{1}\pmod{p}\)。
设:\(p = k \times i + r\),其中 \(1 <r < i < p\) 。换句话说,\(k\) 是 \(p / i\) 的商,\(r\) 是余数。
将这个式子放在 \(\pmod{p}\) 这个意义下,得到 \(k \times i + r \equiv{0\pmod{p}}\)
\(\because k \times i + k \equiv{0\pmod{p}}\)
乘上 \(i^{-1}\),\(r^{-1}\) \(\therefore\) \(k \times r^{-1} + i^{-1} \equiv{0\pmod{p}}\)
移项 $\therefore i^{-1} \equiv -k \times r ^{-1}\pmod{p} $
将,\(k\) 是 \(p / i\) 的商,\(r\) 是余数带入 \(\therefore i^{-1} \equiv -\left\lfloor\dfrac{p}{i}\right\rfloor \times (p \mod i)^{-1} \pmod{p}\)
这样我们就可以利用递推来完成计算逆元的操作了。
注意:因为是存在负数,所以我们取模操作的时候就要 (x + mod) % mod
void solve(){ //线性求逆元
ans[1]=1;
cin >> n >> p;
cout<<1<<endl;
for(int i(2);i <= n;++i){
ans[i] = (-p / i * ans[p % i] + p) % p;
cout<<(ans[i] + p) % p<<endl;
}
}
参考文献:
扩展欧几里得exgcd
扩展欧几里得算法是用来求解 \(ax+by=\gcd(a,b)\) 的一种可行解。
推导过程
设:
\(ax_1 + by_1=gcd(a,b)\)
\(bx_2 + (a\mod b)y_2 = gcd(b,a\mod b)\)。
\(\because gcd(a,b) = gcd(b,a \mod b)\)
\(\therefore ax_1 + by_1 = bx_2 + (a\mod b)y_2\)
又 \(\because a\mod b = a-(\left\lfloor\dfrac{a}{b}\right\rfloor \times b)\)
\(\therefore ax_1 + by_1 = bx_2+[a-(\left\lfloor\dfrac{a}{b}\right\rfloor \times b)]y_2\)
\(\therefore ax_1 + by_1 = bx_2+ay_2-(\left\lfloor\dfrac{a}{b}\right\rfloor \times b)y_2\)
\(\therefore ax_1 + by_1 = ay_2+b\times (x_2-\left\lfloor\dfrac{a}{b}\right\rfloor \times y_2)\)
\(\because a=a ,b=b\)
\(\therefore x_1 = y_2 , y_1 = x_2-\left\lfloor\dfrac{a}{b}\right\rfloor \times y_2\)
一直递归迭代即可。
代码
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 t = x;
x = y;
y = t - (a / b) * y;
return d;
}
线性同余方程
定义
$ax \equiv b \pmod n $
找出这个方程的可行解。( \(x\) 为未知数 )。
Exgcd 解法
首先:可以将式子变为 \(ax+kn = b\)
其中 \(x\) 和 \(k\) 是未知数。对于整数解的充要条件是 \(\gcd(a,n) | b\) 也就是 \(b \mod gcd(a,n) = 0\)
我们先根据 Exgcd 来找出一组 \(x_0\) 和 \(k_0\),\(ax_0+nk_0=gcd(a,n)\) 。
然后等式同除 \(gcd(a,n)\),再乘 \(b\)。
得到:
\(a\dfrac{b}{gcd(a,n)}x_0 + n\dfrac{b}{gcd(a,n)}k_0 = b\)
那么这就是这个方程的可行解。
当: \(gcd(a,n) = 1\) 时 \(x_0\) 与 \(k_0\) 是 \(ax+kn = b\) 的一组解,那么:
\(x = x_0 + nt\)
\(k = k_0 - nt\)
为啥,我就不知道了。这对于任意整数 \(t\) 都是正确的
一般来说,题目让你求最小解,所以只需要求 \((x \mod t + t) \mod t\)
其中就有:\(t = \dfrac{n}{gcd(a,n)}\)
code
int ex_gcd(int a, int b, int& x, int& y) {
if (b == 0) {
x = 1;
y = 0;
return a;
}
int d = ex_gcd(b, a % b, x, y);
int temp = x;
x = y;
y = temp - a / b * y;
return d;
}
bool liEu(int a, int b, int c, int& x, int& y) {
int d = ex_gcd(a, b, x, y);
if (c % d != 0) return 0;
int k = c / d;
x *= k;
y *= k;
return 1;
}
中国剩余定理(CRT)
对于 \(k\) 个同余式,求最小非负的 \(x\)。
第 \(i\) 个同余式。
\(x\equiv{a_i\pmod{m_i}}\)
对于这个方程组需要解 \(x\)。
题目保证所有 \(m\) 两两互质。
设 \(M\) 为所有模数的积,模数都是 \(m+i\)。\(M_i = M / m_i\),\(t_i \times M_i\equiv{1\pmod{m_i}}\) 也就是一个逆元,所以直接Exgcd 搞定 \(t_i\)。。
\(x\equiv \sum_{i=1}^n{a_iM_it_i} \pmod M\)
这样就可以求得\(x\)的最小解了。
我们要证明 \(x\) 在任意 \(i (1 \le i \le k)\) 条式子中是成立的
在 \(i\) 不等于 \(j\) 的时候,有 \(M_j\equiv{0\pmod{m_i}}\) 。
所以 \(c_j \equiv M_j\equiv{0\pmod{m_i}}\) ,
又有 \(c_i \equiv M_i \times (M_i^{-1} \mod m_i) \equiv 1 \pmod {m_i}\)
所以我们有:
\(x \equiv \sum_{i=1}^n{a_iM_it_i} \pmod {m_i}\)
\(\equiv {a_iM_i\times(m_i^{-1}} \mod {m_i}) \pmod {m_i}\)
\(\equiv a_i \pmod {m_i}\)
奥管他推的对不对,知道公式不就是了!!
#include <bits/stdc++.h>
using namespace std;
#define ll long long
const ll N = 1e5+5;
ll n;
ll pr[N],A[N];
ll exgcd(ll a,ll b,ll &x,ll &y){
if(!b){
x=1,y=0;
return a;
}
ll g = exgcd(b,a%b,y,x);
y -= a/b*x;
return g;
}
ll crt(){
ll ans=0;
ll M=1;
for(int i =1;i<=n;i++) M*=pr[i];
for(int i = 1;i <= n;i++){
ll m=M/pr[i];
ll x,y;
exgcd(m,pr[i],x,y);
ll t= m*x%M*A[i]%M;
ans = (ans + t) % M;
}
return (ans+M) %M;
}
int main(){
cin >> n;
for(int i = 1;i <= n;i++){
scanf("%lld%lld",&pr[i],&A[i]);
}
printf("%lld\n",crt());
return 0;
}
拓展中国剩余定理(EXCRT)
认为还是金钩爷讲的好,证明其实感觉。。。哎算了,就把证明发这了。反正正常用直接套公式。
现在考虑合并两个同余方程的时候该怎么做。
$ \begin{cases} x \equiv b \pmod {a} \ x \equiv B \pmod {A} \end{cases} $
这个可以写作:
$ \begin{cases} x = ay + b \ x = AY + B \end{cases} $
由于\(x\)是一个常量,因此\(ay+b=AY+B\),其中\(a,b,A,B\)都是常数
整理一下后就是\(ay+A(-Y)=B-b\)
通过exgcd,可以获得一组关于\(ay+A(-Y)=\gcd(a,A)\)的解\((y_0,-Y_0)\)
即\(a(y_0 \times \frac{B-b}{\gcd(a,A)})+A(-Y_0 \times \frac{B-b}{\gcd(a,A)})=\gcd(a,A) \times \frac{B-b}{\gcd(a,A)}=B-b\)
由于\(x=ay+b\),现在要最小化\(x\),也就是最小化\(y\),即最小化\(t=y_0 \times \frac{B-b}{\gcd(a,A)}\)
由于\(t\)的通解为\(t+\frac{A}{\gcd(a,A)}k\),因此\(t\)的最小值为\(t_0=t \bmod \frac{A}{\gcd(a,A)}\)
于是就可以将这两个同余方程合并为
$ x \equiv a \times t_0+b \pmod {\mathbb{lcm}(a,A)} $
参考文献:https://www.luogu.com.cn/blog/KingSann/solution-p4777。
对于洛谷 P4777 的模板代码如下:
signed n;
int A, B, a, b, d, x, y;
void exgcd(int &x, int &y, int a, int b) {
if (!b) {
d = a, x = 1, y = 0;
} else exgcd(y, x, b, a % b), y -= a / b * x;
}
void merge() {
exgcd(x, y, a, A);
int c = B - b;
if (c % d) {
cout << -1;
exit(0);
} else {
x = ((x * c / d) % (A / d) + (A / d)) % (A / d);
int m = a * A / __gcd(a, A);
b = ((a * x + b) % m + m) % m ;
a = m;
}
}
void excrt() {
cin >> n;
for (int i = 1; i <= n; i++) {
ll x, y;
cin >> x >> y;
A = x, B = y;
if (i != 1) merge();
else a = x, b = y;
}
cout << (ll)(b % a);
}
本文来自博客园,作者:gsczl71,转载请注明原文链接:https://www.cnblogs.com/gsczl71/p/17870948.html
gsczl71 AK IOI!RP = INF 2024年拿下七级勾!