QwQ
整除
对于整数n,mn,m,如果m!=0 且存在整数k,使得km=n,那么m就能整除n,记为m|nm|n
整除具有传递性a|b b|c−−>a|ca|b b|c−−>a|c
素数
素数有无限多个
素数定理
当xx很大的时候,小于x的素数个数近似
算术基本定理
任何一个大于1的正整数n
n=pr11∗prr22....n=p1r1∗pr2r2....
素数判定
解法1:
枚举2到n−−√n的所有正整数
O(n−−√n)
解法2:
枚举2到n−−√n的所有素因子
O(n−−√/lnxn/lnx)
解法3:
Miller-Rabin算法
质因数分解
解法1:朴素求法
for (int i=1;i*i<=n;i++)
{
if (n%i==0){
d[++dsz]=i;
if (i*i<n) d[++dsz]=n/2
}
}
如果我们将n分解成n=pr11∗prr22....n=p1r1∗pr2r2....这种形式,那么nn的约数个数就通过乘法原理求出来
筛法
1.埃式筛法(线性筛的弱化版)
就是每个数标记它的倍数
for (int i=2;i<=n;i++)
{
for (int j=i*2;j<=n;j+=i) check[j]=1;
}
调和剂数(QwQ貌似刚刚还是筛法呢)
1+12+13+14+....1n1+12+13+14+....1n=
1+12+12+14+14+14+14....1+12+12+14+14+14+14.... = log2nlog2n
线性筛
理论依据,就是每一个数只会被自己的最小的质因子筛到
当 i 能整除 prime[j],那么 i*prime[j+1] 这个合数肯定被 prime[j] 乘以某个数筛掉。
for (int i=2;i<=n;i++)
{
if (!check[i]) prime[++tot]=i;
for (int j=1;j<=tot;j++)
{
if (i*prime[j]>n) break;
check[i*prime[j]]=1;
if (i%prime[j]==0) break;
}
}
一个小题:给定n(n<10000)n(n<10000)个数,判定他们是素数和合数
解法:对于一个数xx,直接枚举
另一道小题:
spoj prime1:
给定l,r(l≤r≤109,r−l≤105),求[l,r]内的素数l,r(l≤r≤109,r−l≤105),求[l,r]内的素数
解法:!!区间筛
1>把n−−√n范围内的素数筛出来,然后把这些质数在区间内的倍数筛掉
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
inline int read()
{
int x=0,f=1;char ch=getchar();
while (!isdigit(ch)) {if (ch=='-') f=-1; ch=getchar();}
while (isdigit(ch)) {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
return x*f;
}
const int maxn = 3e5+1e2;
int check[maxn];
int prime[maxn];
int n,m,l,r;
int tot;
int oi[maxn];
void primee(int n)
{
check[1]=1;
for (int i=2;i<=n;i++)
{
if (!check[i]) prime[++tot]=i;
for (int j=1;j<=tot;j++)
{
if (i*prime[j]>n) break;
check[i*prime[j]]=1;
if (i%prime[j]==0) break;
}
}
}
void solve(int l,int r)
{
for (int i=1;i<=tot;++i)
{
int p = prime[i];
if (prime[i]>r) break;
for (int j=(l-1)/p+1;j<=r/p;++j) if (p*j!=prime[i]) oi[p*j-l+1]=1;
}
}
int t;
int main()
{
primee(50000);
cin>>t;
//for (int i=1;i<=10;i++) cout<<prime[i]<<" "<<endl;
while (t--)
{
memset(oi,0,sizeof(oi));
l=read(),r=read();
solve(l,r);
if (l==1) oi[1]=1;
for (int i=1;i<=r-l+1;++i)
{
if (!oi[i]) printf("%d\n",i+l-1);
}
cout<<"\n";
}
return 0;
}
}
QwQ欧几里得算法(gcd)
这里就不记笔记了
裴蜀定理
关于x,y的方程ax+by=cax+by=c 当且仅当gcd(a,b)|cgcd(a,b)|c可知有无穷多解,同时一定存在整数x,y使ax+by=gcd(a,b)ax+by=gcd(a,b)
引理:aa与的线性组合集中最小的正元素是gcd(a,b)gcd(a,b)
引理:若a|bc且(a,b)=1,则a|ca|bc且(a,b)=1,则a|c
算术基本定理:
一些性质:
对于两个数m,nm,n
如果k=gcd(m,n)k=gcd(m,n) 则 kp=min(mp,np)kp=min(mp,np)
如果k=lcm(m,n)k=lcm(m,n)则
kp=max(mp,np)kp=max(mp,np)
小题:
hdu4497
给定三个数的gcd和lcm,求三元组个数(规定了三元组是有序的)
做法:
我们对于每个质因子单独考虑,假设当前的因子是p,那么p的次数一定是大于等于gcd中p的次数,小于等于lcm
因为三元组是有序的,所以我们考虑每两个数成为gcd和lcm的,另一个数在(pgcd,plcm)(pgcd,plcm)之间,那么这种时候就是6×(r−l−1)6×(r−l−1),然后考虑有两个点在端点的情况,因为是对称的,所以最终答案就是6×(r−l+1)+3+3=6×(r−l)6×(r−l+1)+3+3=6×(r−l)
代码后补
拓展欧几里得
QwQ就是求解形如ax+by=gcd(a,b)ax+by=gcd(a,b)的方程
我们首先递归到底部,然后我们要求
ax+by=gcd(a,b)ax+by=gcd(a,b)
然后我们已知了是这个方程的解
bx1+(a−a/b∗b)y1=gcd(b,amod b)bx1+(a−a/b∗b)y1=gcd(b,amod b)
进行一波操作
ay1+b(x1−a/b∗y1)ay1+b(x1−a/b∗y1)
所以x=y1,y=x1−a/b∗y1x=y1,y=x1−a/b∗y1
对于要求所以解的情况,我们只需要让x=x0+bgcd(a,b)x=x0+bgcd(a,b)
y=y0−agcd(a,b)y=y0−agcd(a,b)
小题:noip2012同余方程
求关于 x 的同余方程 ax≡1(modb)ax≡1(modb) 的最小正整数解。
根据同余我们可以得到
ax+by=1ax+by=1
然后直接求解就行
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#define ll long long
using namespace std;
long long a,b,x,y;
ll exgcd(ll &x,ll &y,ll a,ll b)
{
if (b==0)
{
x=1;
y=0;
return a;
}
int ans=exgcd(x,y,b,a%b);
int tmp = x;
x=y;
y=tmp-a/b*y;
}
int main()
{
scanf("%lld%lld",&a,&b);
int ans=exgcd(x,y,a,b);
cout<<(x+b)%b;
return 0;
}
poj1061 青蛙的约会
NOI2002 荒岛野人
逆元
对于任意正整数a满足(a,m)=1,存在b满足ab≡1(modm)ab≡1(modm)
那么b就是a在%m意义下的逆元!
处理阶乘的逆元
q[1]=1;
for (int i=2;i<=n;i++) q[i]=q[i-1]*i%mod;
p[n]=qsm(q[n],mod-2);
for (int i=n;i>1;i--) p[i-1]=p[i]*i%mod;
O(n)预处理逆元!O(n)预处理逆元!
假设我们要求的是i在mod p意义下的逆元
….后补
剩余类和完系
QwQ懒得记录了,还是后补吧
费马小定理
设p是一个素数,a是一个整数,且不是p的倍数,那么
老师课件的简单的证明
可惜费马小定理的逆定理是不成立的
经测试,前10亿的自然数中,用费马小定理的逆定理验证素数,出错的概率是0.000025
QwQ那么我们怎么判断素数呢
这里引入二次探测定理
若p是素数,x是一个正整数,且x2mod p=1x2mod p=1 那么x≡±1(modp)x≡±1(modp)
听亢神的话
用2,7,61,24251做底数