【笔记】很基础的数论知识
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.比例化简
我们发现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
高精度
高精度的加减乘法都是挺简单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)
•使用快速幂就可以了