数论
老久以前的了,以前忘放上面了(差点丢了/jk)
欧拉筛素数
\(O(n)\)筛法
for(int i = 2; i <= n; i++) {
if(vis[i] == 0) pre[tot++] = i;//存下每个素数
for(int j = 0; j <= tot, i * pre[j] <= n; j++){
vis[pre[j] * i] = 1;
if (i % pre[j] == 0){//保证每个数只筛一次
break;
}
}
}
保证每个数被它的最小质因子筛掉
为什么 (i % pre[j] == 0) 就 break ??
证明
-
\(pre\) 数组中的素数是递增的
-
当 \(i\) 能整除 \(pre_j\),那么 \(i * pre_{j+1}\) 这个合数肯定被 \(pre_j\) 乘以某个数筛掉
-
因为 \(i\) 中含有 \(pre_j\) , \(pre_j\) 比 \(pre_{j+1}\) 小,即 \(i = k * pre_j\)
-
那么 \(i * pre_{j+1} = (k * pre_j) * pre_{j + 1}\)
-
设 \(pre_{j + 1} * k = k'\) 所以 \(i * pre_{j +1} = k'*pre_j\)
同余
在模意义下,加减乘都不会影响答案
加减很显然
证明乘??
-
目标:\((x*y) ~mod~ p~= (x ~mod~ p) * (y ~mod~ p)\)
-
\(x = a_1*p + b_1,y = a_2 * p + b_2\)
-
\(x*y = (a_1*a_2 * p + a_1 * b_2 + b_1*a_2)*p + b_1*b_2\)
-
很显然 \(x*y\) 对 \(p\) 取模就是 \((b_1*b_2)\) 对 \(p\) 取模
栗题
我们把最后组合起来的数拆成每一位相加的形式,因为是在模9的意义下,所以 \(10^n~mod~ 9 = 1\) 所以最后答案也就是把每一位相加都取模
辗转相除法证明
-
\(gcd(a, b) = gcd(b, a~mod~b)\)
-
设 \(g|a\),\(g|b\), \(r = a - k * b = a~mod~b\)
-
可得 \(\frac{r}{g}= \frac{a}{g} - \frac{k*b}{g}\)
-
看得出,右边显然是个整数,所以 \(g | r\)
exgcd
可解方程
求 \(x,y\)
-
设 \(a' = b, b' = a~mod~ b\)
-
\(a'x + b'y = gcd(a', b')\)
-
根据 \(gcd\) 得 \(gcd(a', b') = gcd(a, b)\)
-
\(a'x + b'y = gcd(a, b)\)
-
\(bx + (a - b * \lfloor\frac{a}{b} \rfloor)*y = gcd(a, b)\)
-
\(ay + b(x - \lfloor \frac{a}{b}\rfloor*y) = gcd(a,b)\)
\(\therefore x = y, y = x - \lfloor \frac{a}{b}\rfloor*y\)
一个回溯的过程
应用(扩欧解不定方程)
解形如 \(ax+by=c\) 的不定方程
先根据 \(exgcd\) 求出 \(ax + by = gcd(a, b)\) 的一组解 \(x_0, y_0\)
根据裴蜀定理,当 \(c ~mod~ gcd(a, b) = 0\) 的时候,此方程有解
求解
-
设 \(d = gcd(a , b)\)
-
方程两边同时乘以 \(\frac{c}{d}\) , 得到 \(a*\frac{c}{d}x_0 + b*\frac{c}{d}y_0 = c\)
-
然后就有了一组解 \(x_1 = \frac{c}{d}x_0, x_2 = \frac{c}{d}y_0\)
求最小整数解(使 \(x_1\) 最小化)
现在已经由 \(exgcd\) 求得任意一组 \(x, y\) 了
为了满足等式成立
那么 \(x\) 最小正整数解为 \((x + b) \% b\)
为什么??
可以这样想:
先在已经求出一组解使得 \(ax + by = 1\) 也就是 \(y = \frac{1 - ax}{b}\)
因为 \(y\) 是整数,所以此时 \(b | 1 - ax\) 现在想让 \(x\) 改变,如果 \(x\) 改变的值不是 \(b\) 的倍数,那就会使得 \(1 - ax\) 不能整除 \(b\) ,\(y\) 就不是整数了,所以 \(x\) 的改变量一定是 \(b\) 的倍数才可以
code
d = exgcd(a, b, x, y);//返回的是 a,b 的 gcd
if(c % d == 0)
{
x *= c / d;
t = b / d;
t = abs(t);
x = (x % t + t) % t;
printf("%d\n", x);
}
栗题
/*
work by:Ariel_
*/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#define int long long
using namespace std;
int read(){
int x = 0,f = 1; char c = getchar();
while(c < '0'||c > '9') {if(c == '-') f = -1; c = getchar();}
while(c >= '0' && c <= '9') {x = x*10 + c - '0'; c = getchar();}
return x*f;
}
int x_1, y_1;
int exgcd(int a, int b, int &x1, int &y1){
if(!b){
x_1 = 1, y_1 = 0;
return a;
}
int d = exgcd(b, a % b, x_1, y_1);
int t = x_1; x_1 = y_1, y_1 = t - a / b * y_1;
return d;
}
int x, y, m, n, l;
signed main(void){
x = read(), y = read(), m = read(), n = read(), l = read();
int a = n - m, b = l, c = x - y;
int d = exgcd(a, b, x_1, y_1);
if(c % d == 0) {
x_1 *= c / d;
int t = b / d;
t = abs(t);
x_1 = (x_1 % t + t) % t;
printf("%lld", x_1);
}
else printf("Impossible");
return 0;
}
中国剩余定理
若 \(m_1, m_2, m_3……m_n\) 是两两互质的正整数,有同余方程组
\[\begin{cases} x \equiv a_1 (mod~m_1)\\ x \equiv a_2 (mod~m_2)\\ ……\\ x \equiv a_n (mod~m_n)\\ \end{cases} \]求 \(x\) ?
\(M = \prod_{i=1}^{n} m_i\), \(M_i = M/m_i\),\(t_i\) 是线性同余方程 \(M_it_i \equiv 1(mod ~m_i)\) 的一个解
则 \(x = \sum^{n}_{i = 1}a_iM_it_i\)
证明:
根据一个定理
两数不能整除,若除数扩大(或缩小)了几倍,而被除数不变,则其商和余数也同时扩大(或缩小)相同的倍数(余数必小于除数)
(证明: 假设 a % b = d,a / b = k, a = kb + d, a - kd = d,a,b 同时扩大 z 倍,得到 z(a - kd) = zd;)
所以可以得出 \(a_iM_it_i \equiv a_i(mod~~m_i)\)
显然 $ M_i$ 除了可以 \(m_i\) 之外都可以整除其他 \(m\)
\(\because \forall k \not= i,a_iM_it_i = 0(mod~m_k)\)
\(\because a_iM_it_i\equiv a_i(~mod~m_i)\)
所以带入原式得到:\(x = \sum^{n}_{i = 1}a_iM_it_i\)
可以结合 despair_ghost 这篇博客理解,讲的特别好
实现
回到上面的式子
-
\(M_it_i \equiv 1(mod ~m_i)\)
-
可以得到 \(M_it_i - k*m = 1\)
-
\(M_i, m\) 都是定值,因为 \(M_i, m\) 互质,所以就可以 \(exgcd\) 求解了
/*
work by:Ariel_
*/
#include<iostream>
#include<cstdio>
#include<cstring>
#define int long long
using namespace std;
const int N = 20;
int read(){
int x = 0,f = 1; char c = getchar();
while(c < '0'||c > '9') {if(c == '-') f = -1; c = getchar();}
while(c >= '0' && c <= '9') {x = x*10 + c - '0'; c = getchar();}
return x*f;
}
int Ans, M = 1;
int n, m[N], a[N];
void exgcd(int a, int b, int &d, int &x, int &y){
if (!b){d = a,x = 1, y = 0;return;}
exgcd(b, a % b, d, x, y);
int t = x; x = y, y = t - (a / b) * y;
}
void Int_China() {
int Mi, x, y, d;
for (int i = 1; i <= n; i++) {
Mi = M / m[i];
exgcd(Mi, m[i], d, x, y);
Ans = ((Ans + x * Mi * a[i]) % M + M) % M;
}
}
signed main(void) {
n = read();
for(int i = 1; i <= n; i++) m[i] = read(), a[i] = read(), M *= m[i];
Int_China();
printf("%lld\n",(Ans + M) % M );
return 0;
}
扩展CRT
求 \(x\) ?
和中国剩余定理类似,就是没有互质的条件了
数学归纳法:
假设已经求出前 \(k-1\) 个方程组成的同余方程组的一个解为 \(x\)
且有 \(M=\prod_{i=1}^{i\leq k-1}\)
则前 \(k-1\) 个方程的方程组通解为 \(x+i*M(i\in Z)\)
那么对于加入第 \(k\) 个方程后的方程组
我们就是要求一个正整数 \(t\),使得 \(x+t*M \equiv a_k(mod~m_k)\)
转化一下上述式子得 \(t*M \equiv a_k-x(\mod m_k)\)
对于这个式子我们已经可以通过扩展欧几里得求解t
若该同余式无解,则整个方程组无解, 若有,则前 \(k\) 个同余式组成的方程组的一个解解为\(x_k=x+t*M\)
乘法逆元
解决模意义下没法进行除法的问题
已知 \(a|b\) 求 \(\frac{b}{a}mod~m\)
设法把除法转化到乘法上去
找一个 \(a^{-1}\) 满足 $a*a^{-1}\equiv1(mod~m) $
换个形式
\(ax \equiv 1(mod~m)\)
根据扩展中国剩余定理就可以求出来了
可与忽视 p 不为质数的情况:只要 gcd(a,m) 不为 1,那么 a 的逆元就不存在
欧拉定理求逆元
\(inv[a] = a^{\phi(p) - 1}\) (保证 \(a\) 和 \(p\) 互质)
欧拉函数
欧拉函数 \(\varphi(m)\) 表示不超过 \(m\) 且和 \(m\) 互素的正整数的个数
\(m = \prod p_i^{a_i}\)
求单值欧拉函数?
公式:
\(p_1,p_2…p_k\) 为 \(n\) 的所有质因子
证明
考虑容斥
因为要求与 \(n\) 互质的数的个数,因为在 \(n\) 之内,每一个 \(p_i\) 的倍数分布是均匀的, \(n\) 之内有 \(\frac{1}{p_i}\) 的数是 \(p_i\) 的倍数,因此有 \(n*(1 - \frac{1}{p_i})\) 个数不是 \(p_i\) 的倍数,同理有 \(n*(1 - \frac{1}{p_j})\) 个数不是 \(p_j\) 的倍数,根据乘法原理,有 \(n*(1 - \frac{1}{p_i})* (1 - \frac{1}{p_j})\) 个数既不是 \(p_i\) 的倍数,也不是 \(p_j\) 的倍数。进而就可以推广得到有 \(n*\prod^{k}_{i = 1}(1 - p_i)\) 个数与 \(n\) 互质 (有点生物算遗传的意思)
严谨证明(来自 \(attack\) 大佬)
1.当 \(n=1\) 时
很明显,答案为 \(1\)
2.当 \(n\) 为质数时
根据素数的定义,答案为 \(n−1\)
3.当 \(n\) 为合数时
我们已经知道了 \(n\) 为素数的情况
对 \(n\) 进行质因数分解
设 \(n=a^{p_{1}^1}∗a^{p_{2}^{2}}...∗a^{p_{k}^k}\)
假设 \(k=1\)
那么 \(\varphi(p^k)=p^k−p^{k−1}\)
证明:考虑容斥,与一个数互素的数的个数就是这个数减去与它不互素的数的个数
因为 \(p\) 是素数,所以在 \(p^k\) 中与其不互素的数为 \(1∗p,2∗p....p^{k−1}∗p\),有\(p^{k−1}\)个
当 \(k\neq 1\) 时
-
\(\varphi(n) = \varphi(a_1^{p_{1}^1}∗a_2^{p_{2}^{2}}...∗a_k^{p_{k}^k})\)
-
\(\prod^k_{i = 1}a_i^{p_i} - a_{i}^{p_{i - 1}}\)
-
\(\prod^k_{i = 1}a_i^{p_i}(1-\frac{1}{p_i})\)
-
\(n*\prod^k_{i = 1}(1-\frac{1}{p_i})\)
单值求法:若 \(gcd(a, b) = 1\) 就更新 \(phi_n\) 的值
求 \(1-n\) 的 \(\varphi(i)\) ?
埃氏筛
\(O(n)\) 扫一遍,每扫到一个质数,就更新一下它的所有的倍数
时间复杂度:\(O(n*ln ln n)\)
/*
work by:Ariel_
*/
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int N = 1e5 + 4;
int read(){
int x = 0,f = 1; char c = getchar();
while(c < '0'||c > '9') {if(c == '-') f = -1; c = getchar();}
while(c >= '0' && c <= '9') {x = x*10 + c - '0'; c = getchar();}
return x*f;
}
int n, phi[N];
int main(void){
n = read();
for (int i = 1; i <= n; i++) phi[i] = i;
for (int i = 2; i <= n; i++){
if(phi[i] == i) {
for (int j = i; j <= n; j += i)
phi[j] = phi[j] / i * (i - 1);
}
}
for(int i = 1; i <= n; i++){
printf("%d\n", phi[i]);
}
return 0;
}
康托展开
康托展开是全排列到自然数的一一映射,该自然数可以代表该序列在全排列中的排名(从0开始)
正康托展开:全排列到名次
\(a_5 = \lbrace2,3,5,1,4\rbrace\)
求该序列的名次
考虑比它小的序列
\(1*4!+1*3!+2*2!+0+0 = 34\) 排列组合
可以这么理解:后面比当前小的数的个数*后面数的全排列
逆康托展开:名次到全排列
\(1*4!+1*3!+2*2!+0+0 = 34\)
得到 \(a_5 = \lbrace2,3,5,1,4\rbrace\)
卢卡斯定理/Lucas 定理
这直接给出公式,证明过程涉及二项式内容,感性理解 = =
\(C_n^m = Lucas(n, m, p)\)
\(Lucas(n, m, p) = C(n\%p,m\%p) * Lucas(\frac{n}{p }, \frac{m}{p}, p)\)
\(Lucas(x, 0, p) = 1, C(a, b) = (\frac{a!}{b!\times(a - b)!}^{p - 2})~mod~p\)
int n, m, p, a[N];//a:阶乘
int qpow(int x, int y) {
int tmp = 1;
while(y) {
if (y & 1) tmp = (tmp * x) % p;
y >>= 1;
x = (x * x) % p;
}
return tmp;
}
int C(int n, int m) {
if (m > n) return 0;
return (a[n] * qpow(a[m], p - 2) % p * qpow(a[n - m], p - 2) % p) % p;
}
int Lucas(int n, int m) {
if (!m) return 1;
return C(n % p, m % p) * Lucas(n / p, m / p) % p;
}
signed main(){
int T = read();
while(T--) {
a[0] = 1;
n = read(), m = read(), p = read();
for (int i = 1; i <= p; i++) a[i] = a[i - 1] * i % p;
printf("%lld\n", Lucas(n + m, n));
}
return 0;
}
整数分块
求 \(\sum_{i = 1}^{n}(\lfloor \frac{n}{i}\rfloor)^5 \times i\)
\(n \leq 10^9\) 答案对 \(10^9 + 7\) 取模。
引理一:$\lfloor \frac {n}{i} \rfloor $ 最多只有 \(2\sqrt{n}\) 种不同的取值。
证明:将 \(i\) 分为大于等于 \(\sqrt{x}\) 与大于 \(\sqrt{x}\) 的两部分
- 当 \(d \leq \sqrt{x}\) 时
此时 \(\lfloor{\frac{x}{d}}\rfloor \geq \sqrt{x}\),最多 \(\sqrt{x}\) 种取值
- 当 \(d > \sqrt{x}\) 时
\(\lfloor \frac{x}{d} \rfloor < \sqrt{x}\), 最多 \(\sqrt{x}\) 种取值
复杂度为 \(O(\sqrt{x})\)
引理二:所有$\lfloor \frac {n}{i} \rfloor $ 相同的对应的 i 一定是一段连续的区间
反证法:如果不是连续的区间
一定存在 \(l < t < r\) 使得 \(\lfloor \frac{n}{l}\rfloor = \lfloor \frac{n}{r}\rfloor\) 且\(\lfloor \frac{n}{l}\rfloor \neq \lfloor \frac{n}{t}\rfloor\)
\(\because t > l\)
\(\because \lfloor \frac{n}{t}\rfloor < \lfloor \frac{n}{l}\rfloor\)
\(\because t < r\)
\(\because \lfloor \frac{n}{t}\rfloor > \lfloor \frac{n}{r}\rfloor\)
所以假设不成立
引理三:对于所有连续的区间,如果知道了 l ,那么 r 也会知道
$r = \lfloor{\frac{n}{\lfloor\frac{n}{l}\rfloor}}\rfloor $
证明?我不会
//f(a) = a^5;
for (int i = 1, last; i <= n; i = last + 1) {
int a = n / i;
last = n / a;
ret += f(a) * (sum[last] - sum[i - 1]);
}

浙公网安备 33010602011771号