质数
质数的定义
质数(prime number)又称素数,有无限个。一个大于
1的自然数,除了
1和它本身外,不能被其他自然数整除;否则称为合数。根据算术基本定理,每一个比1大的整数,要么本身是一个质数,要么可以写成一系列质数的乘积。
判断质数的方法
第一种:暴力筛选
思路分析
根据质数的定义,我们可以简单地想到:若要判断n是不是质数,我们可以直接写一个循环(
i从
2到
n,进行
n运算,即
n能不能被
1整除,如被整除即不质数。若所有的
i都不能整除,
n即为质数)。
代码实现
CPPbool prime(int n){
for(int i=2;i<=sqrt(n);i++){
if(n%i==0){
return false;
}
}
return true;
}
时间复杂度O(n)
第二种:埃拉托斯特尼(Eratosthenes)筛法
思路分析
创建一个比范围上限大1的数组,我们只关注下标为
1∼N(要求的上限) 的数组元素与数组下标对应。将数组初始化为
1。然后用for循环,遍历范围为:
[2∼n]。如果数组元素为
1,则说明这个数组元素的下标所对应的数是质数。随后我们将这个下标(除
1以外)的整数倍所对应的数组元素全部置为
0,也就是判断其为非质数。这样,我们就知道了范围内
(1∼范围上限
N)所有数是质数(下标对应的数组元素值为
1)或不是质数(下标对应的数组元素值为
0)
例子(N=25)
-
列出
2以后的所有序列:
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
-
将序列中,划掉
2的倍数,序列变成:
2 3 5 7 9 11 13 15 17 19 21 23 25
-
如果这个序列中最大数小于最后一个标出的质数的平方,那么剩下的序列中所有的数都是质数,否则回到第二步。
-
本例中,因为
25大于
2的平方,我们返回第二步:
-
剩下的序列中第一个质数是
3,将主序列中
3的倍数划掉,主序列变成:
2 3 5 7 11 13 17 19 23 25
-
我们得到的质数有:2,3
-
25仍然大于
3的平方,所以我们还要返回第二步:
-
序列中第一个质数是
5,同样将序列中
5的倍数划掉,主序列成:
2 3 5 7 11 13 17 19 23
-
我们得到的质数有:2 3 5
-
结论:
2到
25之间的质数是:
2 3 5 7 11 13 17 19 23
代码实现
CPPbool prime(int n){
memset(v,0,sizeof v);
for(int i=2;i<=n;i++){
if(v[i]) continue;
cout<<i<<endl;
for(int j=1;j<=n/i;j++) v[i*j]=1;
}
}
时间复杂度O(NloglogN)
第三种:线性筛选–欧拉筛法
思路分析
在埃拉托斯特尼(Eratosthenes)筛法的基础上,让每个合数只被它的最小质因子筛选一次,以达到不重复的目的。
代码实现
CPPvoid prime(){
memset(v,0,sizeof v);
m=0;
for(int i=2;i<=n;i++){
if(v[i]==0){
v[i]=i;
prime[++m]=i;
}
for(int j=1;j<=m;j++){
if(prime[j]>v[i] || prime[j]>n/i) break;
v[i*prime[j]]=prime[j];
}
}
for(int i=1;i<=m;i++) cout<<prime[i]<<endl;
}
算术基本定理
任何一个大于
1的正整数都能唯一分解为有限个质数的乘积,可以记作:
N=p1c1p2c2…pmcm
而其正约数集合可以记作:
{p1b1p2b2…pmbm} 其 中
0⩽bi⩽ci
其正约数个数可以记作:
(C1+1)(C2+1)(C3+1)⋯(Cm+1)=∏i=1m(Ci+1)
其正约数之和可以记作:
(1+p1+p12+⋯+p1c1)⋯(1+pm1+pm2+⋯+pmcm)=∏i=1m(∑j=0ci(pi)j)
阶乘中因子出现的次数
存在
2 的有:
2 4 6 8 10 12 14 16 18 20 共有
10 个
部分数还有:
4 8 12 16 20共有
5 个。
发现还有:
8 16 再记录
2 个。
所以最后就有 10+5+2+1=18 个。
也就是说,我们如果将这样的行为用公式表示。可以总结出:
如果要求
n!中因子
x 的出现次数,那
x 就出现了
⌊xn⌋+⌊x2n⌋+⌊x3n⌋+…+⌊xmn⌋ 次。
约数
约数的定义
若整数
n除以整数
d的余数为
0,即
d能整除
n,则称
d是
n的约数,
n是
d的倍数,记为
d∣n
求正约数集合的方法
第一种:试除法
适用于单个正整数求正约数集合
思路分析
若
d≥N是
N的约数,则
dN≤N也是
N的约数。所以我们可以说约束总是成对出现的(完全平方数中
N会单独出现)
所以我们只需要扫描
d=1∼N,尝试
d是否能整除
N,若能整除,则
dN也是
N的约数。
代码实现
CPPfor(int i=1;i*i<=n;i++){
if(n%i==0){
factor[++m]=i;
if(i!=n/i) factor[++m]=n/i;
}
}
for(int i=1;i<=m;i++){
cout<<factor[i]<<endl;
}
第二种:倍数法
思路分析
若再次采用上述的试除法的思路来完成的话,那么时间复杂度将会来到惊人的
O(NN)
所以我们可以反过来思考,对于每个数
d,
1∼N中以
d为约数的数就是
d的倍数
代码实现
CPPvector<int> factor[500010];
for(int i=1;i<=n;i++){
for(int j=1;j>=n/i;j++){
factor[i*j].push_back(i);
}
}
for(int i=1;i<=n;i++){
for(int j=0;j<factor[i].size();j++){
cout<<factor[i][j]<<" ";
}
}
取模的性质
a%b=a−b×⌊ba⌋
最大公约数
定义
若自然数
d同时是自然数
a和
b的约数,曾称
d是
a和
b的公约数。在所有
a和
b的公约数中最大的一个,称为
a和
b的最大公约数,记为
gcd(a,b)
若自然数
d同时是自然数
a和
b的倍数,曾称
d是
a和
b的公倍数。在所有
a和
b的公倍数中最小的一个,称为
a和
b的最小公倍数,记为
lcm(a,b)
性质
∀a,b∈N gcd(a,b)×lcm(a,b)=a×b
证明:
设
d=gcd(a,b),
a0=da b0=db 根据最大公约数的定义,
gcd(a0,b0)=1
根据最小公倍数的性质,
lcm(a0,b0)=a×b
所以则有
lcm(a,b)=lcm(a0×d,b0×d)=lcm(a0,b0)×d=a0×b0×d=da×b
证毕
求最大公约数的方法
第一种:暴力出奇迹
从大到小暴力枚举。
第二种:辗转相除法
gcd(a,b)=gcd(a,a%b)
可以使用递归求解
时间复杂度
O(log(a+b))
第三种:更相减损术
如果
a≥b,则有
gcd(a,b)=gcd(b,a−b)=gcd(a,a−b)
第四种:二进制法
如果
a,b 大到需要使用高精度时,因为高精度取模不容易实现,所以考虑使用更相减损术的一个扩展方法。
我们可以先讨论
a,b 的奇偶性。如果是两个奇数,更相减损一次,如果是一奇一偶,偶数
÷2,否则均
÷2,结果
×2
互质和欧拉函数
定义
∀a,b∈N ,若
gcd(a,b)=1 ,则称
a,b互质。
对于三个数或更多个数的情况,我们把
gcd(a,b,c)=1 的情况称为
a,b,c 互质。
欧拉函数
1~N 中与
N 互质的数的个数被称为欧拉函数,记为
φ(N) 。
我们可以将其应用在唯一分解定理中,
N=p1c1p2c2⋯pmcm ,则:
φ(N)=N×p1p1−1×p2p2−1×⋯×pmpm−1=N×∏质数 p∣N(1−p1)
证明:
设
p 是
N 的质因子,
1∼N中 p 的倍数有
p,2p,3p,⋯,(pN)×p,共
pN 个。
同理,若
q 也是
N 的质因子,则
1∼N 中
q的倍数有
pN 个。
如果我们把这
pN+pN 个数去掉,那么
p×q 的倍数被排除了两次,通过容斥原理,我们需要加回来一次。
因此,
1∼N 中不与
N含有共同质因子
p 或
q 的数的个数为:
N−pN−qN+pqN=N×(1−p1−q1+pq1)=N(1−p1)(1−q1)
证毕。
性质1~2
1.
∀n>1,1∼n中与
n 互质的数的和为
2n×φ(n)。
φ(ab)=φ(a)φ(b) 。
证明:
因为
gcd(n,x)=gcd(n,n−x) ,所以与
n 不互质的数
x,
n−x 成对出现,平均值为
2n。因此,与
n互质的数的平均值也是
2n ,进而得到性质
1 。
根据欧拉函数的计算式,对
a,b 分解质因数,直接可得性质
2。
证毕。
积性函数
如果当
a,b 互质时,有
f(ab)=f(a)×f(b) ,那么称函数
f 为积性函数。
性质3~6
3.若
f 是积性函数,且在算术基本定理中
n=∏i=1mpici ,则
f(n)=∏i=1mf(pici)。
4.设
p 为质数,若
p∣n 且
p2∣n ,则
φ(n)=φ(pn)×p 。
5.设
p 为质数,若
p∣n 但
p2∤n,则
φ(n)=φ(pn)×(p−1)。
6.
∑d∣nφ(d)=n
证明:
把
n 分解质因数,按照积性函数的定义,性质
3 一眼成立。
若
p∣n且
p2∣n ,则
n,pn 包含相同的质因子,只是
p 的指数不同。直接把
φ(n)与φ(pn)。
φ(n)=n×(1−p11)×(1−p21)×(1−p31)×⋯×(1−pm1)
φ(n)=pn×(1−p11)×(1−p21)×(1−p31)×⋯×(1−pm1)
二者相除,商为
p ,所以性质
4成立。
若
p∣n 但
p2∤n ,则
p,
pn 互质,由
φ是积性函数得
φ(n)=φ(pn)∗φ(p) ,而
φ(p)=p−1 ,所以性质
5成立。
设
f(n)=∑d∣nφ(d) 。用乘法分配律展开比较,再利用
φ 是积性函数,得到:若 n, m 互质,则
f(nm)=∑d∣nmφ(d)=(∑d∣nφ(d))×(∑d∣mφ(d))=f(n)×f(m) 。
即
∑d∣nφ(d) 是积性函数。
对于单个质因子
f(pm)=∑d∣pmφ(d)=φ(1)+φ(p)+φ(p2)+⋯+φ(pm)
是一个等比数列求和再加
1,结果为
pm。所以
f(n)=∏i=1mf(pici)=∏i=1mpici=n ,性质
6 成立。
证毕。
同余
同余的定义
若整数
a 和整数
b 除以正整数
m 的余数相等,则称
a,b 模
m 同余,记为
a≡b(modm) 。
同余类与剩余系
对于
∀a∈[0,m−1] ,集合
{a+km}(k∈Z) 的所有数模
m 同余,余数都是
a 。该集合称为一个模
m 的同余类,简记为
aˉ 。
不难发现模
m 的同余类一共有
m 个,分别为
0,1,2,⋯,m−1 。它们构成
m 的完全剩余系。
1~m 中与
m 互质的数代表的同余类共有
φ(m) 个,它们构成
m 的简化剩余系。例如,模
10 的简化剩余系为
{1,3,7,9} 。
简化剩余关于模
m 乘法封闭。这是因为若
a,b(1≤a,b≤m) 与
m 互质,则
a×b 也不可能与
m 含在相同的质因子,即
a×b 也与
m 互质。再由余数的定义即可得到
a×bmodm 也与
m 互质,即
a×bmodm 也属于
m 的简化剩余系。
欧拉定理
若正整数
a,n 互质,则
aφ(n)≡1(modn)
证明:
设
n 的简化剩余系为
{a1,a2,⋯,aφ(n)} 。
对
∀ai,aj ,若
a×ai≡a×aj(modn) ,则
a×(ai−aj)≡0 。
因为
a,n 互质,所以
ai−aj≡0 ,即
ai≡aj 。
所以
ai=aj 时,
aai,aaj 也代表不同的同余类。
又因为简化同余系关于模
n 乘法封闭,故
aai 也在简化剩余系集合中。因此,集合
{a1,a2,⋯,aφ(n)} 与 集合
{aa1,aa2,⋯,aaφ(n)} 都能表示
n 的简化剩余系。
所以
aφ(n)a1a2⋯aφ(n)≡(aa1)(aa2)⋯(aaφ(n))≡a1a2⋯aφ(n)(modn)
因此
aφ(n)≡1(modn) 。
证毕。
费马小定理
若
p 是质数,则对于任意整数
a ,有
ap≡a(modp) 。
对于费马小定理我们可以直接通过欧拉定理来证明
证明过程参照裴蜀定理。
裴蜀定理
对于任意整数
a,b ,存在一对整数
x,y ,满足
ax+by=gcd(a,b) 。
证明:
在欧几里得算法的最后一步,即
b=0 时,显然有一对整数
x=1,y=0 ,使得
a×1+0×0=gcd(a,0) 。
若
b>0 ,则
gcd(a,b)=gcd(b,amodb) 。
假设存在一对整数
x,y ,满足
b×x+(amodb)×y=gcd(b,amodb) ,
因为
bx+(amodb)y=bx+(a−b⌊ba⌋)y=ay+b(x−⌊ba⌋y) ,
所以令
x′=y,y′=x−⌊ba⌋y,
我们就得到了
ax′+by′=gcd(a,b) 。
证毕。
所以,只要
a 不是
p 的倍数,就有
ap−1≡1(modp) ,两边同乘
a 就是费马小定理。
证毕。
矩阵乘法与高斯消元
条件
- 矩阵 A 的列数必须等于矩阵 B 的行数。
- 如果 A 是一个 n×m 的矩阵, B 必须是一个 m×p 的矩阵。
- 结果矩阵 C 的维度为 n×p 。
定义
矩阵
A(n×m) 和矩阵
B(m×p) 的乘积
C(n×p) 定义为:
Ci,j=∑k=1mAi,k×Bk,j
其中:
- Ci,j 是结果矩阵 C 的第 i 行第 j 列的元素。
- Ai,k 是矩阵 A 的第 i 行第 k 列的元素。
- Bk,j 是矩阵 B 的第 k 行第 j 列的元素。
意义
矩阵相乘的定义来源于实际问题的需求。例如:
- 方程组求解:线性方程组可以表示为矩阵形式Ax=B ,其中 A是系数矩阵, x是未知数向量, B是常数向量。矩阵相乘用于表示方程组的线性组合,在接下来的总结中,会详细讲到。
- 数据分析:在机器学习中,矩阵相乘用于表示特征和权重的线性组合。
矩阵相乘满足线性代数中我们所说的运算律
- 结合律: (A×B)×C=A×(B×C) 。
- 分配律: A×(B+C)=A×B+A×C 。
我们实现矩阵乘法的代码如下:
CPPstruct matrix{
int a[101][101];
matrix(){
memset(a,0,sizeof a);
}
};
matrix operator * (const matrix &A,const matrix &B){
matrix c;
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
for(int k=1;k<=n;k++){
c.a[i][j]=(c.a[i][j]+A.a[i][k]*B.a[k][j])%M;
}
}
}
return c;
}
矩阵快速幂
还是上面的例子,虽然
[1221]3 并不难算。但是计算
[1221]108 的时候,这就已经跑不动了。因为矩阵乘法本身就有一个
O(nmk) ,导致他会有一个
O(8) 的常数。
证明矩阵快速幂可行性,等价于需要证明出在矩阵中的
A×B×C=A×(B×C) ,而这就是结合律,是显然得到的。
代码如下:
CPPwhile(k){
if(k&1) ans=ans*a;
a=a*a;
k>>=1;
}
高斯消元法求解方程组
在解决这个问题之前我们先引入增广矩阵和矩阵的初等行变换的概念
增广矩阵
增广矩阵就是在系数矩阵的右边添上一列,这一列是方程组的等号右边的值
例如:
a11a21⋮an1a12a22⋮an2a13a23⋮an3⋯⋯⋯a1na2n⋮annb1b2⋮bn
矩阵的初等行变换
- 交换两行
- 把某一行乘一个非 0 的数
- 把某行的若干倍加到另一行上去
高斯消元的步骤与我们平常求解多元一次方程组的方法一样,举个例子:
我们有二元一次方程组
{2x+3y=403x+2y=45
首先我们将一式的主元
x的系数先化为
1,则有
x+1.5y=20
对于电脑我们可以使用
double类型进行存储
然后我们将这个化简完的一式与二式做差得到
2.5y=15
解出来就可以得到方程组的解
{x=11y=6
我们将上述的方法稍作总结可以得到:
方程组初始的形式
x1,1x2,1…xn,1x1,2x2,2xn,2………x1,n∣x1,n+1x2,n∣x2,n+1xn,n∣xn,n+1
方程组结束的形式
x1,1′0…0x1,2′x2,2′0………x1,n′∣x1,n+1′x2,n′∣x2,n+1′xn,n′∣xn,n+1′
按照上面的推导过程,我们不难得到以下实现代码:
高斯消元法
CPPbool guass(){
for(int i=1;i<=n;i++){
for(int k=i;k<=n;k++){
if(fabs(a[k][i])>eps){
swap(a[i],a[k]);
break;
}
}
if(fabs(a[i][i])<eps) return 0;
for(int j=n+1;j>=1;j--) a[i][j]/=a[i][i];
for(int k=i+1;k<=n;k++){
for(int j=n+1;j>=i;j--) a[k][j]-=a[i][j]*a[k][i];
}
}
for(int i=n-1;i>=1;i--){
for(int j=i+1;j<=n;j++) a[i][n+1]-=a[i][j]*a[j][n+1];
}
return 1;
}
组合计数基础
加法原理
若完成一件事情的方法共有
n类,其中第
i个步骤有
ai种完成方法,并且这些方法都不重合,则完成这件事情的方法一共有
a1+a2+⋯+an种,我们称这个为加法原理。
乘法原理
若完成一件事情的方法共有
n类,其中第
i个步骤有
ai种完成方法,并且这些方法都互相之间没有干扰,则完成这件事情的方法一共有
a1×a2×⋯×an种,我们称这个为乘法原理。
排列数
从
n 个不同元素中依次取出
m 个元素排成一列,产生的不同排列的数量为:
Anm=(n−m)!n!=n×(n−1)×⋯×(n−m+1)
组合数
从
n 个不同元素中取出
m 个组成一个集合,产生的不同集合数量为:
Cnm=m!(n−m)!n!=m×(m−1)×⋯×2×1n×(n−1)×⋯×(n−m+1)
我们发现排列数和组合数只有分母上有一个
m!的差别因为组合数是不考虑顺序的,相对于排列数来说会有
m!个重复的方法,所以我们需要除以
m!
性质
-
Cnm=Cnn−m
-
Cnm=Cn−1m+Cn−1m−1
-
Cn0+Cn1+Cn2+⋯+Cnn=2n
-
Cn0=Cnn=1
-
Cnm=m!n×(n−1)×⋯×(n−m+1)
证明:
性质1:
将性质1代入公式,发现等式恒成立,两边都为
m!(n−m)!n!
性质2:
从
n 个不同元素中取出
m 个组成一个集合有两类方法:取
n 号元素,不取
n 号元素。若取
n 号元素,则应在剩余
n−1 个元素中选出
m−1 个,有
Cn−1m−1 种取法。若不取
n 号元素,则应在剩余
n−1 个元素中选出 m 个,有
Cn−1m 种取法。再根据加法原理,即可证明。
性质3:
我们可以将等式左边理解为一种包含了选和不选全部方案的写法,而等式右边则是另外一种相同意义的方法。即可证得。
性质4:
在
n种方案中全部选取和全部不选取都是一种方案。
性质5:
根据组合数和排列数的关系可以知道
Cnm=m!Anm,而
Anm=n×(n−1)×⋯×(n−m+1)这是上面排列数的定义由来的,将上述的第一个等式中的
Anm替换成
n×(n−1)×⋯×(n−m+1)即可证明此性质。
组合数的求法
根据性质 2,用递推法可求出
0≤y≤x≤n 的所有组合数
Cxy ,复杂度
O(n2) 。
代码如下:
CPPvoid get(){
for(int i=0; i<N; i++) C[i][0] = 1;
for(int i=1; i<N; i++){
for(int j=1; j<=i; j++){
C[i][j]=(C[i-1][j]+C[i-1][j-1])%P;
}
}
}
组合数的结果一般较大,若题目要求出
Cnm 对一个数
p 取模后的值,并且
1∼n 都存在模 p 乘法逆元,则可以先计算分子
n!modp ,再计算分母
m!(n−m)!modp 的逆元,乘起来得到
Cnmmodp ,复杂度为
O(n) 。
代码如下:
CPPf[0]=g[0]=1;
for(int i=1;i<N;i++){
f[i]=f[i-1]*i%P;
g[i]=qsm(f[i],P-2)%P;
}
二项式定理
(a+b)n=∑k=0nCnkakbn−k
意义
在二项式展开中,组合数
Cnk的含义是:
从
n个因子
a+b中,选取
k个
b ,其余
n−k个选择
a的方式有多少种。
这正是组合数的定义。
Lucas 定理
若
p 是质数,则对于任意整数
1≤m≤n ,有:
Cnm≡Cnmodpmmodp×Cn/pm/p(modp)
也就是把
n 和
m 表示成
p 进制数,对
p 进制下的每一位分别计算组合数,最后再乘起来。
证明等学了生成函数以后再来补上。
Lucas 算法主要应用于
m和
n很大,但是
p很小的情况,不然相对于原来的处理方式没有变化。
代码如下:
CPPint a[1000001];
int pow(int a,int k,int p){
int ans=1;
while(k){
if(k&1) ans=(ans*a)%p;
a=(a*a)%p;
k>>=1;
}
return ans%p;
}
int C(int n,int m){
if(m>n) return 0;
return ((a[n]*pow(a[m],p-2,p))%p*pow(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;
}
void solve(){
int n,m;
cin>>n>>m>>p;
a[0]=1;
for(int i=1;i<=p;i++) a[i]=(a[i-1]*i)%p;
cout<<Lucas(n,m)<<endl;
}
容斥原理
为了使重叠部分不被重复计算,人们研究出一种新的计数方法,这种方法的基本思想是:先不考虑重叠的情况,把答案计算出来,然后再把重复计算的数目排斥出去,这种计数的方法称为容斥原理。——度娘
看起来确实很高端,但是,我们有一个十分简单的理解方法去理解这个这个原理。
举个例子:
求
1 到
n 中能被
3 或
5 或
7 整除的数。
这道题我们首先需要在
1到
n中先筛选能被
3 或
5 或
7 整除的数,但是如果这样就结束的话,显然像
15和
21和
35这样的数是被算了两次的,像
105甚至我们算了三次,这样肯定不是正确的。
我们需要县减去
15和
21和
35再加上
105就可以得到正确的答案。
这就是容斥原理,其实理解起来不难。
我们想要的是上面这张图里全部的信息。
但是我们发现,电脑无法一下实现,所以我们决定先给它传入
A,B,C,把这些加起来,再减去
A⋂B,
A⋂C 和
B⋂C。
这个时候,我们又发现,中间的
A⋂B⋂C 被多减去了一次,所以加回来。
这也就完成了上面我们举的例子的步骤。
在信息学中,使用容斥原理需要注意的是以下几点:
初始答案
状态设计
容斥方法
其实思路都是和数学上的差不了多少。
莫比乌斯函数
设正整数
N 按照算术基本定理分解质因数为
N=p1c1p2c2⋯pmcm ,定义函数
μ(N)=⎩⎨⎧01−1∃i∈[1,m],ci>1m≡0(mod2),∀i∈[1,m],ci=1m≡1(mod2),∀i∈[1,m],ci=1
称
μ(N) 为
Mo¨bius 函数(莫比乌斯函数)。
通俗地讲,当
N 包含相等的质因子时,
μ(N)=0 。
若
N 有偶数个质因子,
μ(N)=1 。
若
N 有奇数个质因子,
μ(N)=−1 。
也就是说,我们可以通过莫比乌斯函数记录当前节点对结果的影响是怎样。
也就是说,如果
μ(N)=0,根本无法构造,
μ(N)=1 时为正,
μ(N)=−1 时为负。
和欧拉函数一样,它们都属于积性函数,所以我们可以和欧拉函数一样使用欧拉筛把莫比乌斯函数筛出来。
代码如下:
CPP#include<bits/stdc++.h>
using namespace std;
const int maxn = 5e4+10;
int prime[maxn],cnt;int mu[maxn];
bitset<maxn> vis;
void Init(int N){
mu[1]=1;
for(int i=2;i<=N;++i){
if(!vis[i]) prime[++cnt]=i,mu[i]=-1;
for(int j=1;j<=cnt&&i*prime[j]<=N;++j){
vis[i*prime[j]]=1;
if(i%prime[j]==0) break;
mu[i*prime[j]]=mu[i]*-1;
}
}
}
int t,a,b,d;
int main(){
Init(5e4);
for(int i=1;i<=1000;i++){
cout<<i<<" "<<mu[i]<<endl;
}
return 0;
}
其实这个章节本该还有莫比乌斯反演的,但是郭总说需要和后面的知识相互配合着学习,所以莫比乌斯反演这一段先放着,等学完之后在来完工