数论初步
-
整除
则b整除a,也就是b是a的因数
相关性质:
传递性:若,,则
习题
[CF 762A] k-th divisor 题目链接求 n 的第 k 小的约数。如果不存在输出 -11 ≤ n ≤ , 1 ≤ k ≤
由于约数总是成对出现,且一个不大于
,与之对应的另一个则不小于
所以我们需要枚举 1,2,... ,
代码:
#include<bits/stdc++.h>
#define lld I64d //这句很重要!! 第五个点的数据较大!
using namespace std;
long long n,k;
int d[31622777];
signed main()
{
scanf("%lld%lld",&n,&k);
int cnt=0;
for(int i=1;i<=n/i;i++)
if(n%i==0) d[++cnt]=i;
if(k<=cnt) printf("%d\n",d[k]);
else
{
k-=cnt;
if(d[cnt]==n/d[cnt]) k++;
if(k>cnt) printf("-1");
else printf("%lld\n",n/d[cnt+1-k]);
}
return 0;
}
-
质数
若大于
1
的正整数
p
仅有两个因子
1
和
p
,则称
p
是一个质数(素数)。
否则,若
p
>
1
,则称
p
是一个合数。
特别地,1
不是质数也不是合数 。
相关性质:
若 n 是一个合数,则 n 至少有 1 个质因子。因此其中最小的质因子一定不大于
质数有无穷多个。不大于 n 的质数约有 个。
唯一分解定理:把正整数 n 写成质数的乘积 (即 n = p1p2p3...pk,其中 pi 为质数且单调不减), 这样的表示是唯一的。
Eratosthenes算法
筛法的思想很简单,就是对于每个不超过的n的非负整数p,删除2p,3p,... ,当处理完所有的数以后,没有被删除的数字就是质数。这样就能构建出一张素数表
时间复杂度 O(nlogn) 时间复杂度的证明
bool notPrime[maxn];
void prime()
{
memset(notPrime, false, sizeof notPrime);
notPrime[0] = notPrime[1] = true;
for (int i = 2; i <= n; i++)
for (int j = i*2; j <= n; j += i)
notPrime[j] = true;
}
习题
[CF 776B] Sherlock and his girlfriendn 个点,标号 2 ... n + 1 ,给这些点染色,要求若 a 是 b 的质因子,则 a 和 b 的颜色不同。求一种颜色数最少的方案 ,n ≤ 1000
-
带余除法
对于整数
a
,
b
,
b
>
0
,则存在唯一的整数
q
,
r
,满足
a
=
bq
+
r
,
其中
0
≤
r
<
b
。
其中称
q
为商、
r
为余数。
余数用
a mod b
(
a
%
b
)
表示。
若两数
a
,
b
除以
c
的余数相等,则称
a
,
b
模
c
同余,记做
a
≡
b
(
mod c
)
。
相关性质
与
等价
推论:
,d|c,则
-
最大公约数
设
a
,
b
是不都为
0
的整数,
c
为满足
c
|
a
且
c
|
b
的最大整数,
则称
c
是
a
,
b
的最大公约数
记为 gcd
(
a
,
b
)
gcd代码实现
long long gcd(ll a, ll b)
{
long long t;
while(b)
{
t=b;
b=a%b;
a=t;
}
return a;
}
习题
[CF 664A] Complicated GCD求 gcd ( a , a + 1 , a + 2 , ..., b )1 ≤ a ≤ b ≤
因为gcd(a,a+1)=1 所以当a<b时,答案就是1,否则,就是a
代码:
#include <bits/stdc++.h>
using namespace std;
string a,b;
int main()
{
cin>>a>>b;
if (a==b) cout<<a<<endl;
else cout<<1<<endl;
return 0;
}
[CF 757B] Bash’s Big Day给定 n 个正整数 { a i } ,求一个子集 S ,满足 gcd ( S 1 , ..., S k ) > 1 ,同时 | S | 尽可能大。1 ≤ n , ai ≤
对于每一个子集S,若其满足
gcd
(
S
1
, ...,
S
k
)
>
1的性质,则说明它们有一个公因子k(质数),而这道题的ai范围不大,可以直接枚举k,也就是0 ... max{ai} ,然后直接计算能被k整除的元素个数
注:100000以内大约有不到10000个质数
-
欧几里得算法
又称辗转相除法
迭代求两数
gcd
的做法
由
(
a
,
b
) = (
a
,
ka
+
b
)
的性质:
gcd
(
a
,
b
) =
gcd
(
b
,
a mod b
)
时间复杂度O
(
log
n
)
while(b != 0){
temp = a;
a = b;
b = t % a;
}
-
裴蜀定理
裴蜀定理:
设
(
a
,
b
) =
d
,则对任意整数
x
,
y
,有
d
|
(
ax
+
by
)
成立;
特别地,一定存在
x
,
y
满足
ax
+
by
=
d
等价的表述:不定方程 ax + by = c(a, b, c 为整数) 有解的充要条件为 (a, b)|c
推论:a, b 互质等价于 ax + by = 1 有解
-
扩展欧几里得
基本算法:对于不完全为 0 的非负整数 a,b,gcd(a,b)表示 a,b 的最大公约数,必然存在整数对 x,y ,使得 gcd(a,b)=ax+by。
设求出
bx
+
ry
=
d
的一个解为
x
=
x
0
,
y
=
y
0
,考虑如何把它变形成
ax
+
by
=
d
的解。
将
a
=
bq
+
r
代入
ax
+
by
=
d
,化简得
b
(
xq
+
y
) +
rx
=
d
我们令
xq
+
y
=
x
0
,
x
=
y
0
,则上式成立
故
x
=
y
0
,
y
=
x
0-
y
0
q
为
ax
+
by
=
d
的解
停止情况:b=0时,x=1.y=0
扩欧模板题
#include<bits/stdc++.h>
using namespace std;
long long x,y;
void exgcd(long long a, long long b)
{
if(b==0)
{
x=1;
y=0;
return;
}
exgcd(b,a%b);
long long temp=x;
x=y;
y=temp-a/b*y;
}
int main()
{
long long n,m;
scanf("%lld%lld",&n,&m);
exgcd(n,m);
printf("%lld",(x%m+m)%m);
return 0;
}
-
逆元
如果ax≡1 (mod p),且gcd(a,p)=1(a与p互质),则称a关于模p的乘法逆元为x。
1.扩欧求逆元
hdu1576
要求(A/B)%9973,但由于A很大,我们只给出n(n=A%9973)(我们给定的A必能被B整除,且gcd(B,9973) = 1)。
#include<bits/stdc++.h>
using namespace std;
const int mod=9973;
long long n,b,x,y;
int t;
void exgcd(long long a, long long b)
{
if(b==0)
{
x=1; y=0;
return;
}
exgcd(b,a%b);
long long temp=x;
x=y;
y=temp-a/b*y;
}
int main()
{
scanf("%d",&t);
while(t--)
{
scanf("%lld%lld",&n,&b);
exgcd(b,mod);
x=(x%mod+mod)%mod;
printf("%lld\n",x*n%mod);
}
return 0;
}
2.费马小定理求逆元
long long quickpow(long long a,long long b)
{
if(b<0) return 0;
long long ret=1;
a%=mod;
while(b)
{
if(b & 1) ret = ( ret *a ) % mod
b>>=1;
a = (a * a)% mod;
}
return ret;
}
long long inv(long long a)
{
return quickpow(a,mod-2);
}
3.递推求逆元
洛谷 P3811
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#define int long long
using namespace std;
int n,p,ans[3000005];
signed main()
{
scanf("%lld%lld",&n,&p);
ans[1]=1;
printf("1\n");
for(int i=2;i<=n;i++)
{
ans[i]=(((-(p/i)*ans[p%i])%p)+p)%p;
printf("%lld\n",ans[i]);
}
return 0;
}