数论–提高课
质数
性质:
- 1-n质数个数:N/lnN
- 分解质因数:N!中p的次数–N/p + N/p^2 + … + N/p^x[下取整]
- 非全等数列不可能既是等差又是等比
埃氏筛:
复杂度– O ( n l o g l o g n ) O(nloglogn) O(nloglogn)
代码:
void get_primes1(){
for(int i=2;i<=n;i++)
{
if(!st[i])
{
primes[cnt++]=i;
for(int j=i;j<=n;j+=i) st[j]=true;//利用质数倍增将合数筛掉
}
}
}
线性筛(欧筛):
代码:– O ( n ) O(n) O(n)
- 利用质因子 p r i m e prime prime将 i ∗ p r i m e i * prime i∗prime筛掉
void get_primes(){
//外层从2~n迭代,因为这毕竟算的是1~n中质数的个数,而不是某个数是不是质数的判定
for(int i = 2;i <= n;i ++)
{
if(!st[i]) primes[cnt ++]=i;
for(int j = 0; primes[j] * i <= n; j ++)
{//primes[j]*i<=n,把大于n的合数都筛了就没啥意义了
st[primes[j] * i] = true;//用最小质因子去筛合数
//1)当i%primes[j]!=0时,说明此时遍历到的primes[j]不是i的质因子,那么只可能是此时的primes[j]<i的
//最小质因子,所以primes[j]*i的最小质因子就是primes[j];
//2)当有i%primes[j]==0时,说明i的最小质因子是primes[j],因此primes[j]*i的最小质因子也就应该是
//prime[j],之后接着用st[primes[j+1]*i]=true去筛合数时,就不是用最小质因子去更新了,因为i有最小
//质因子primes[j]<primes[j+1],此时的primes[j+1]不是primes[j+1]*i的最小质因子,此时就应该
//退出循环,避免之后重复进行筛选。
if(i % primes[j] == 0) break;
}
}
}
约数
性质:
- 则N的约数个数为:
设 N 分解质因子为: N = p 1 a 1 ∗ p 2 a 2 ∗ ⋯ ∗ p n a n 其中 p 1 , p 2 , ⋯ , p n 为 N 的质因子 则 N 的约数个数为: f ( N ) = ( a 1 + 1 ) ∗ ( a 2 + 1 ) ∗ ⋯ ∗ ( a n + 1 ) 设N分解质因子为: \\N = p_1^{a_1} * p_2^{a_2} *\cdots*p_n^{a_n}\\其中p_1,p_2,\cdots,p_n为N的质因子\\ 则N的约数个数为:f(N) = (a_1 + 1) * (a_2 + 1) * \cdots* (a_n + 1) 设N分解质因子为:N=p1a1∗p2a2∗⋯∗pnan其中p1,p2,⋯,pn为N的质因子则N的约数个数为:f(N)=(a1+1)∗(a2+1)∗⋯∗(an+1)
-
**1-N所有数的约数个数之和:**NlogN
∑ i = 1 N f ( i ) = N 1 + N 2 + N 3 + ⋯ + N N = N ∗ ( 1 + 1 2 + 1 3 + ⋯ + 1 N ) ≈ N l o g N \sum_{i = 1}^{N}{f(i)} = \frac{N}{1} + \frac{N}{2} + \frac{N}{3} + \cdots+\frac{N}{N} \\=N*(1 + \frac{1}{2} + \frac{1}{3} + \cdots + \frac{1}{N}) \\ \approx NlogN i=1∑Nf(i)=1N+2N+3N+⋯+NN=N∗(1+21+31+⋯+N1)≈NlogN -
int范围内f(i)__max = 1600.
代码:
for(int i = 2; i <= num / i; i++ )
{
while(num % i == 0)
{
num /= i;
primes[i] ++;
}
}
//最后num!=1,那么num此时为一个质数
if(num > 1) primes[num] ++;
}
LL ans = 1;
for(auto p : primes) ans = (p.second + 1) * ans % mod;
求组合数
1. void init()//阶乘求逆元
{
infac[0] = fac[0] = 1;
rep(i, 1, N - 10)
{
fac[i] = fac[i - 1] * i % mod;
infac[i] = qmi(fac[i], mod - 2);
//infac[i] = infac[i - 1] * qmi(i, mod - 2) % mod; 这个也可以求阶乘逆元
}
}
2. void init()//递推式
{
C[0][0] = 1;
rep(i, 1, 60)
rep(j, 0, i)
{
C[i][j] = C[i - 1][j];
if(j) C[i][j] += C[i - 1][j - 1];
}
}
卡特兰数
任意前缀0的个数>=1的个数–卡特兰数!!
ps:看见第三项为5是大概率就是卡特兰数
C 2 n n − C 2 n n − 1 = C 2 n n n + 1 C_{2n}^{n}-C_{2n}^{n-1} = \frac{C_{2n}^{n}}{n+1} C2nn−C2nn−1=n+1C2nn
欧拉函数
Euler定理:
设 a , m ∈ N + a, m \in \mathbb{N^+} a,m∈N+ 且 gcd(a,m) = 1
则有: a ϕ m ≡ 1 ( % m ) a^{\phi_{m}} \equiv 1 (\%m) aϕm≡1(%m)
这个可以延伸出费马小定理。
性质:
欧拉函数
ϕ
(
n
)
:
1
−
n
中与
n
互质的数的个数。
N
=
p
1
a
1
∗
p
2
a
2
∗
⋯
∗
p
n
a
n
−
−
p
i
为质因子
ϕ
(
N
)
=
N
∗
(
1
−
1
p
1
)
∗
(
1
−
1
p
2
)
∗
⋯
∗
(
1
−
1
p
n
)
欧拉函数\phi(n):1-n中与n互质的数的个数。 \\ N = p_1^{a_1} * p_2^{a2}*\cdots*p_n^{a_n}--p_i为质因子 \\\phi(N) = N * (1 - \frac{1}{p_1})*(1-\frac{1}{p_2})*\cdots*\ (1-\frac{1}{p_n})
欧拉函数ϕ(n):1−n中与n互质的数的个数。N=p1a1∗p2a2∗⋯∗pnan−−pi为质因子ϕ(N)=N∗(1−p11)∗(1−p21)∗⋯∗ (1−pn1)
Code:
void init(int n)
{
//因为线性筛能将1-n所有数筛且仅筛一遍,故在线性筛的基础上求每个数的欧拉函数
for(int i = 2; i <= n; i ++)
{
if(!st[i])//i此时为质数,则互质的数为i-1个
{
primes[cnt ++] = i;
euler[i] = i - 1;
}
for(int j = 0; primes[j] * i <= n; j ++)//因为primes[j] * i > n,所以将primes[j] * i筛去也没意思
{
st[primes[j] * i] = true;
if(i % primes[j] == 0) //此时primes[j]是i的最小质因子,那么也是primes[j] * i的最小质因子
{
euler[i * primes[j]] = euler[i] * primes[j];
break;
}
//此时primes[j]不是i * primes[j] 的质因子,所以在i的欧拉函数上又多了一个质因子
euler[i * primes[j]] = euler[i] * (primes[j] - 1);
}
}
}
扩展欧几里得
应用:
给a,b以及 d = gcd(a,b)求x0,y0使得:ax0 + by0 = d
求x,y的通解:
x = x 0 + k ∗ b / d x = x_{0} + k * b/d x=x0+k∗b/d
y = y 0 + k ∗ a / d y=y_{0} + k * a/d y=y0+k∗a/d
–> x最小正整数解 = ( x 0 % ( b / d ) + ( b / d ) ) % ( b / d ) (x_{0} \% (b/d) + (b/d)) \% (b/d) (x0%(b/d)+(b/d))%(b/d)
证明:
基本算法:对于不完全为 0 的非负整数 a,b,gcd(a,b)表示 a,b 的最大公约数,必然存在整数对 x,y ,使得 gcd(a,b)=ax+by。
我们观察到:欧几里德算法停止的状态是: a= gcd , b = 0 ,那么,这是否能给我们求解 x y 提供一种思路呢?因为,这时候,只要 a = gcd 的系数是 1 ,那么只要 b 的系数是 0 或者其他值(无所谓是多少,反正任何数乘以 0 都等于 0 但是a 的系数一定要是 1),这时,我们就会有: a1 + b0 = gcd
当然这是最终状态,但是我们是否可以从最终状态反推到最初的状态呢?
假设当前我们要处理的是求出 a 和 b的最大公约数,并求出 x 和 y 使得 ax + by= gcd ,而我们已经求出了下一个状态:b 和 a%b 的最大公约数,并且求出了一组x1 和y1 使得: bx1 + (a%b)y1 = gcd , 那么这两个相邻的状态之间是否存在一种关系呢?
我们知道: a%b = a - (a/b)*b(这里的 “/” 指的是整除,例如 5/2=2 , 1/3=0),那么,我们可以进一步得到:
gcd = b*x1 + (a-(a/b)*b)*y1 = b*x1 + a*y1 – (a/b)*b*y1 = a*y1 + b*(x1 – a/b*y1) 对比之前我们的状态:求一组 x 和 y 使得:a*x + b*y = gcd ,是否发现了什么? 这里: x = y1 y = x1 – a/b*y1 以上就是扩展欧几里德算法的全部过程
作者:incra
链接:https://www.acwing.com/blog/content/15760/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
扩欧求逆元:
a x ≡ 1 ( m o d m ) ax≡1 (mod m) ax≡1(modm),这里,我们称 x 是 a 关于 m 的乘法逆元。
利用扩展欧几里得算法,等价为: ax + my = 1。
我们发现当gcd(a , m) != 1 的时候是没有解的。这也是 ax + by = c 有解的充要条件: c % gcd(a , b) == 0
一般,我们能够找到无数组解满足条件,但是一般是让你求解出最小的那组解,怎么做?我们求解出来了一个特殊的解 x0 那么,我们用 ( x 0 % m + m ) % m (x_0 \% m+m)\%m (x0%m+m)%m其实就得到了最小正整数解了。(x 的通解是 x0 + mt)
Code:
int exgcd(int a, int b, int &x, int &y)
{
if(!b)
{
x = 1, y = 0;
return a;
}
int d = exgcd(b, a % b, y, x);
y -= a / b * x;
return d;
}
同余方程组(中国剩余定理)
形如:
KaTeX parse error: Invalid size: 'ai之间要互质!!' at position 111: …x_n (\%a_n) \\ [̲a̲i̲之̲间̲要̲互̲质̲!̲!̲]̲
则:
设
M
=
a
1
∗
a
2
∗
a
3
∗
⋯
∗
a
n
;
令
M
i
=
M
a
i
,
t
i
是
M
i
关于
a
i
的逆元即:
M
i
∗
t
i
≡
1
(
%
a
i
)
则:
N
=
∑
i
=
1
n
x
i
∗
M
i
∗
t
i
设 M = a_1 * a_2 * a_3 * \cdots * a_n; \\ 令 M_i = \frac{M}{a_i},t_i是M_i关于a_i的逆元即: M_i*t_i\equiv1(\%a_i) \\ 则:N = \sum_{i = 1}^{n}{x_i*M_i*t_i}
设M=a1∗a2∗a3∗⋯∗an;令Mi=aiM,ti是Mi关于ai的逆元即:Mi∗ti≡1(%ai)则:N=i=1∑nxi∗Mi∗ti
博弈论
关于博弈论能用dp推出来的原因:
因为: 博弈论的某个状态一定可以由前面的状态得到, 一定相当于是有向无环图即:拓扑图. 故可以使用记忆化搜索(DP)得到.
容斥原理
trick:容斥原理可以用二进制枚举,看1的个数,来确定正负号
$$
|A\cup B\cup C\cup…\cup K| = |A| + |B| + |C| +…+ |K| \-|A\cap B| - |A\cap C|-…-|A\cap K|\
+|A\cap B\cap C| + … +
\…
\ 即:多个集合的并集=奇数个集合交集的和减去偶数个集合交集的和
$$
Mobius函数
Code:
void init(int n)
{
mobius[1] = 1;
for(int i = 2; i <= n; i ++)
{
if(!st[i])
{
primes[cnt ++] = i;
mobius[i] = -1;//质数:k=1
}
for(int j = 0; primes[j] * i <= n; j ++)
{
st[primes[j] * i] = true;
if(i % primes[j] == 0)
{
//i有一个primes[j]故i*primes[j]:k = 2
mobius[i * primes[j]] = 0;
break;
}
//在i的基础上多了一个primes[j]
mobius[i * primes[j]] = mobius[i] * -1;
}
}
for(int i = 1; i <= n; i ++) sum_mobius[i] = sum_mobius[i - 1] + mobius[i];
}
高斯消元
在n3的时间内求解n元线性方程组
变为上三角矩阵步骤:
枚举列:
- 找主元(当前列绝对值最大的那个)
- 交换(将主元的行换到最上面)
- 归一(将这个主元系数化为1)
- 消(用这个化为1的主元将这一列的系数消去)
5:最后用上三角矩阵求解未知数
代码:
int gauss()
{
//枚举列
int c, r;
for(c = 0, r = 0; c < n; c ++)
{
//找主元
int t = r;
for(int i = r; i < n; i ++)
if(fabs(f[i][c]) > fabs(f[t][c])) t = i;
//该列最大主元为0
if(eps > fabs(f[t][c])) continue;
//交换:将主元行换为第r行
for(int i = c; i <= n; i ++) swap(f[r][i], f[t][i]);
//归一:将主元系数消为1
for(int i = n; i >= c; i --) f[r][i] /= f[r][c];
//消:利用归一后的主元将该行下面的这列系数全消为0
for(int i = r + 1; i < n; i ++)
if(fabs(f[i][c]) > eps)
for(int j = n; j >= c; j --)
f[i][j] -= f[i][c] * f[r][j];
r ++;
}//枚举完列之后,矩阵就变为上三角矩阵
if(r < n)
{
for(int i = r; i < n; i ++)
if(f[i][n] > eps) return 2;//无解: 0 = 非0
return 1;//有无穷多解
}
//求解:将上三角变为答案
for(int i = n - 1; i > 0; i --)
for(int j = i - 1; j >= 0; j --)
f[j][n] -= f[j][i] * f[i][n];
return 0;//有唯一解
}
矩阵乘法板子
int N = ;
struct matrix
{
int n, m;//矩阵的行列
LL num[N + 5][N + 5];
void clear()
{
mem(num, 0);
}
void print()
{
rep(i, 0, n - 1)
{
rep(j, 0, m - 1) cout << num[i][j] << ' ';
cout << endl;
}
}
};
matrix operator + (matrix a, matrix b)
{
rep(i, 0, a.n - 1)
rep(j, 0, a.m - 1) a.num[i][j] = (a.num[i][j] + b.num[i][j]) % mod;
return a;
}
matrix operator * (matrix a, matrix b)
{
matrix ans;
ans.n = a.n, ans.m = b.m;
ans.clear();
rep(i, 0, a.n - 1)
rep(j, 0, b.m - 1)
rep(k, 0, a.m - 1)
ans.num[i][j] = (ans.num[i][j] + a.num[i][k] * b.num[k][j] % mod) % mod;
return ans;
}
matrix qmi(matrix a, LL k)
{
matrix res;
res.n = a.n, res.m = a.m;
res.clear();
rep(i, 0, a.n - 1) res.num[i][i] = 1;
while(k)
{
if(k & 1) res = res * a;
k >>= 1;
a = a * a;
}
return res;
}