【笔记】很基础的数论知识

2020.1.17 qbxt笔记

最大公约数、最小公倍数

定义

如果d是能同时整除a, b中最大的正整数,我们称d为a和b的最大公约数,记作d = gcd(a, b)

如果一个数d,既是a的倍数,又是b的倍数,同时d是满足这两个条件中的最小正整数,那么称d是a和b的最小公倍数,记作d =
lcm(a, b)

辗转相除法

•假设a > b,我们有gcd(a, b) = gcd(b, a % b)

利用这样的方法,可以把求最大公约数的问题转化为两个更小的数的最大公约数,从而完成求解

时间复杂度为log

求得$ gcd(a,b)$之后我们就很容易求出a与b的最小公倍数

$ gcd(a,b) * lcm(a, b) = a * b $

int gcd(int a,int b){
	return b==0?a:gcd(b,a%b); 
}

唯一分解定理

任意一个正整数x,都可以唯一地分解成p1^a1 * p2^a2 * … * pn^an的形式,其中p1到pn是素数(不考虑素数之间的顺序)

时间复杂度为O(log x)

例题

1.NOIP2001 最大公约数和最小公倍数问题

传送

输入两个正整数x, y,问有多少对正整数以x为最大公约数,以y为最小公倍数

x <= 10^5, y <= 10^6

上面我们刚刚提到,$ gcd(a,b) * lcm(a, b) = a * b $

所以我们可以知道x ,y的乘积,只需要枚举x,便可以知道y。

并判断x和y是否满足题意中最大公约数为x,最小公倍数为y

统计满足的x和y的数量即为所求答案

#include<iostream>
#include<math.h>
#include<cstdio>
using namespace std;
int x,y;
int gcd(int a,int b){
	return b==0?a:gcd(b,a%b); 
}
int ans;
int main(){
	scanf("%d%d",&x,&y);//最大公约数 最小公倍数 x*y=p*q;
	for(int i=1;i<=sqrt(x*y);i++){
		if((x*y)%i==0){
			if(gcd(i,(x*y)/i)==x)ans++;
		}
	} 
	cout<<ans*2;
}

2.比例化简

多啦a梦的任意门(滑稽

我们发现L的范围很小,最大直到100。考虑我们枚举答案的分子分母,判断互质。并使A’/B’≥ A/B且A’/B’- A/B的值尽可能小。

#include<bits/stdc++.h>
using namespace std;
int main(){
	float a,b,c;
	cin>>a>>b>>c;
	float ansmin=100,ansa,ansb,sum;
	//cout<<a/b;
	for(float i=1;i<=c;i++){
		for(float j=1;j<=c;j++){
			if(i/j>=a/b){
				sum=i/j-a/b;
				if(sum<ansmin){
					ansa=i,ansb=j;
					ansmin=sum;
				}	
			}	
			//cout<<sum<<" "<<ansmin<<endl;	
		}
	}
	cout<<ansa<<" "<<ansb<<endl;
}

嗯对啦,上面的写法在luogu上能通过,但还存在精度上的问题

我们还可以模拟一下分数的运算过程

ll gcd(ll a,ll b){
		return b == 0 ? a : gcd(b,a % b);
}
struct frac{
		ll a,b;
};
frac operator +(frac x,frac y){
	ll n = x.a * y.b + x.b * y.a;
  	ll m = x.b * y.b;
  	ll z = gcd(m,n);
  	n /= z,m /= z;
  	return (frac){n,m};
}
bool operator <(frac x,frac y){
		return x.a * y.b < x.b * y.a;
}

补充的一些内容

整数

int 范围是-231-1~231-1

long long 范围是-263-1~263-1

unsigned int 范围是0~2^32-1

unsigned long long 范围是0~2^64-1

进制

P进制下的(a1,a2,…an)代表的数字是

(a 1 * p^{(n-1)} + a 2 * p^{(n-2)} + … + an)

嗯对,剩下内容自己看初赛一本通吧(滑稽x

高精度

cyc最爱的高精度

高精度的加减乘法都是挺简单x

至于除法emm(应该也没人会考吧qwq??

但还是补充一下吧

高精度除以低精度

bignum operator/(bignum x,bignum y){
    bignum z;
    z.len = x.len;
    for(int i=z.len;i;i--)
    {
        z.a[i]=x.a[i]/y;
        x.a[i-1]+=10*x.a[i]%y;
    }
    while(z.len>1&&z.a[z.len] == 0)z.len--;
    return z;
}

高精度除以高精度

……我 我算了我放弃了我太难了(以下是代码

bool operator<=(Bignum x,Bignum y)
{
	if(x.len<y.len)return 1;
	if(x.len>y.len)return 0;
	for(int i=x.len;i;i--)
	if(x.a[i]!=y.a[i])
	return x.a[i]<=y.a[i];
	return 1;
}
Bignum shift(Bignum x,int len)
{
	Bignum y;
	y.len=x.len+len;
	for(int i=1;i<=x.len;i++)
	y.a[i+len]=x.a[i];
	return y;
}
Bignum operator/(Bignum x,Bignum y)
{
	Bignum z;
	z.len=x.len;
	for(int i=z.len;i;i--)
	{
		for(int k=9;k>0;k--)
		if(shift(y,i-1)*k<=x)
		{
			z.a[i]=k;
			x=x-shift(y,i-1)*Bignum(k);
			break;
		}
	}
	while(z.len>1&&z.a[z.len]==0)z.len--;
	return z;
}

压位

一个int只存储0到9的数字位,使得我们的程序空间和时间效率都不高

int的最大范围可以达到2147483647,所以一个int存储8位是没问题的

在读入和输出的时候要注意,除了最高位之外要补0

进制转换

假设从p进制转换到q进制

如果一个long long能存下:先转换到10进制再转换到q进制

如果一个long long不能存下:模拟除法除q的过程

同余

同余使我秃头。

如果a和b模m之后的数值是相等的,那么a和b模m同余

OI里很多题目需要取模,需要用同余的结论

•同余是模意义下的等价关系

•很多运算在同余意义下也存在等价的运算

•除法(等价于乘以逆元)

•开根号(等价于求模方程的根)

•对数(等价于离散对数)

逆元

•在模意义下,一个相当于1/a的数

•记a的逆元为a^-1

•逆元满足a * a^-1 = a^-1 * a = 1 (mod p)

•需要掌握模质数的逆元

费马小定理

•a^(p-1) = 1 (mod p)

•所以逆元就是a^(p-2)

•使用快速幂就可以了

扩展欧几里得定理

https://www.cnblogs.com/huixinxinw/p/12207417.html

原文地址:https://www.cnblogs.com/huixinxinw/p/12207433.html