【整除|质数|筛法|gcd|exgcd|费马小定理|裴蜀定理|逆元】数论初步(一)

10 篇文章 0 订阅
1 篇文章 0 订阅

数论初步

  • 整除

b|a 则b整除a,也就是b是a的因数

相关性质:

    传递性:若a|ba|b,b|cb|c,则a|c

习题

[CF 762A] k-th divisor   题目链接
 
n 的第 k 小的约数。如果不存在输出 -1
 
1 n ≤ 10^{15} , 1 k 10^{9}
 
由于约数总是成对出现,且一个不大于 \sqrt{n},与之对应的另一个则不小于 \sqrt{n}
所以我们需要枚举 1,2,... , \sqrt{n}
 
 
代码:
 
#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 个质因子。因此其中最小的质因子一定不大于{\color{DarkOrange} }\sqrt{n}
    质数有无穷多个。不大于 n 的质数约有 \frac{n}{lnn}个。
 
    唯一分解定理:把正整数 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 girlfriend
n 个点,标号 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 )
 
相关性质
 
    a\equiv b(mod c)c|(a-b)等价
    推论: a\equiv b(mod c),d|c,则 a\equiv b(mod d)
 
 
  • 最大公约数

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 _{10}100

 

因为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 _{10}5
对于每一个子集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;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值