部分概念引自百度百科和各位神犇资料,侵删。
无特殊说明,基本都在mod意义下
逆元
定义:
逆元素是指一个可以取消另一给定元素运算的元素,在数学里,逆元素广义化了加法中的加法逆元和乘法中的倒数。
在模运算中 ax=1(mod p)x就是a在%p意义下的逆元。
解法:
1.费马小定理
2.exgcd
参照同余方程一题。
ax=1(mod p)
x即为a的逆元,转成exgcd求解x
费马小定理
内容:
p是质数,gcd(a,p)=1,a^(p-1)=1(mod p);
证明:
感谢某位数学大佬(虽然我看不懂)
应用:
a的逆元即为a^(p-2)
欧拉函数
定义:
φ(i)表示小于i的正整数中与i互质的数的数目,φ(1)=1。
欧拉定理:
费马小定理是欧拉定理的特殊情况,oi用的不多,,,,不写了。
公式:
通式:
解释:
1.当i为质数 φ(i)=i-1;
2.第一行,由上一页的递推式不断展开
3.第二行,可以理解为将n质因数分解,然后把括号里的同*pk,指数-1;
素性判定
定理基础:
利用费马小定理的逆定理,随机a,可以判断p是否为素数,但是,正确率较低。
二次探测定理:
若p 是素数,x 是一个整数,且x^2 mod p 1,那么x =1/-1 (mod p)
以上,都在mod p意义下
第三行,左侧有因数p,那么右侧(x-1)或(x+1)中必定有一部分中有因数p,即某一部分是p的倍数,
x+1是p的倍数:x=-1;
x- 1是p的倍数:x=1;
即:一个质数不存在非平凡平方根(就是除了1,-1没有数x的平方根=质数p)
可以通过类似23^2 = 1 (mod 66) 的条件判断66 不是质数,增
加正确性
Miller Rabin 算法
a可以取2~25内质数,不必要随机。
图1
图2
首先,若p是质数,a^p-1=1,即“然后是一堆奇奇怪怪的1”
显然图1中的a^x序列是等比数列,公比为2;
1只能有1,-1两种情况平方而来,
图2中的第一个判断,去除由1平方到1的情况。(return true表示该数为质数)
第二个判断,去除了由-1平方到1的情况;
剩下的情况:要么不满足费马小,要么由一个非-1的数跳转到1(在mod p意义下是可能的,如23^2=1 mod 66),都是错误的;
中国剩余定理
显然,不证。
R/B,A已知,求k转化为同余方程,用exgcd求解。
积性函数
定义:
如果一个数论函数f(x) 满足对于任意的互质正整数n,m 均有
f(nm) = f(n)f(m), 则称为积性函数。
如果一个数论函数f(x) 满足对于任意的正整数n,m 均有
f(nm) = f(n)f(m), 则称为完全积性函数。
栗子:
模n 意义下的逆元inv(x):完全积性函数
欧拉函数phi(x): 积性函数
因数个数函数: 积性函数
因数和函数: 积性函数
莫比乌斯函数mu(x): 积性函数//这个忽视掉吧qwq
证明:快下课了,不写了(好吧我就是不想写)
用线性筛搞出所有积性函数
先粘一波mhy的代码
#include<iostream>
using namespace std;
#define MAXN 30
bool pflag[MAXN];
int prime[MAXN],topp=-1;
int phi[MAXN];
int g[MAXN],gg[MAXN];
int mnprime[MAXN];
void init()
{
phi[1] = 1;
g[1] = gg[1] = 1;
for (int i=2;i<MAXN;i++)
{
if (!pflag[i])
{
prime[++topp] = i;
phi[i] = i-1;
g[i] = 1;
gg[i] = i+1;
mnprime[i] = i;
inv[i] = pow_mod(i,p-2);
}
for (int j=0;j<=topp && i*prime[j]<MAXN;j++)
{
printf("%d*%d -> %d\n",i,prime[j],i*prime[j]);
pflag[i*prime[j]] = true;
mnprime[i*prime[j]] = prime[j];
inv[i*prime[j]] = inv[i] * inv[prime[j]];
if (prime[j] == mnprime[i])
{
g[i*prime[j]] = g[i];
gg[i*prime[j]] = (gg[i] * prime[j] + 1);
}else
{
g[i*prime[j]] = g[i] * gg[i];
gg[i*prime[j]] = prime[j]+1;
}
if (i%prime[j] == 0)
{
phi[i*prime[j]] = phi[i] * prime[j];
break;
}else
{
phi[i*prime[j]] = phi[i] * (prime[j]-1);
}
}
}
}
int main()
{
freopen("output.txt","w",stdout);
init();
/*
for (int i=0;i<=topp;i++)
printf("%d ",prime[i]);
printf("\n");
*/
for (int i=1;i<MAXN;i++)
{
printf("%d: phi=%d sum=%d g=%d gg=%d\n",i,phi[i],g[i]*gg[i],g[i],gg[i]);
}
}
欧拉函数:
首先:线性筛中的每一个合数,都是被他的最小的质因子筛掉的。
根据欧拉函数的通式,令x=i*prime[j];
若i%prime[j] == 0
,那么i中已有prime j,故prime是x的一个指数不为1的质因子,且phi[i]中已有pk-1,所以prime对phi[x]的贡献为公式中的指数+1,即prime[j]
若不为0,那么i中没有prime,prime是x的一个指数为1的质因子,对phi x贡献为prime-1
void init()
{
phi[1] = 1;
for (int i=2;i<MAXN;i++)
{
if (!pflag[i])
{
prime[++topp] = i;
phi[i] = i-1;//质数的欧拉函数值
}
for (int j=0;j<=topp && i*prime[j]<MAXN;j++)
{
pflag[i*prime[j]] = true;
if (i%prime[j] == 0)
{
phi[i*prime[j]] = phi[i] * prime[j];
break;
}else
{
phi[i*prime[j]] = phi[i] * (prime[j]-1);
}
}
}
}
逆元:
完全积性函数,直接*
因数和:
质数的因数和=本身+1;
维护一个x的最小质因子mnprime,g为x除了mnp以外的质因子的因数和的乘积,gg为mnp的因数和。
void init()
{
g[1] = 1;
gg[1] = 1;
for (int i=2;i<MAXN;i++)
{
if (!pflag[i])
{
prime[++topp] = i;
g[i] = 1;
gg[i] = i+1;
mnprime[i] = i;
}
for (int j=0;j<=topp && i*prime[j]<MAXN;j++)
{
pflag[i*prime[j]] = true;
mnprime[i*prime[j]] = prime[j];//被最小的质因子筛
if (prime[j] == mnprime[i])
{
g[i*prime[j]] = g[i];//除了mnp的因数和之积不变
gg[i*prime[j]] = (gg[i] * prime[j] + 1);
//以2为例,本来是(1,2),多了一个2,质因子变成了4,因数和变成了(1,2,4),即本来的*2,再加上2^0=1;
}else
{
g[i*prime[j]] = g[i] * gg[i];//g加入了以前的gg,*起来
gg[i*prime[j]] = prime[j]+1;//gg更新为当前的prime+1
}
if (i%prime[j] == 0) break;
}
}
}
筛法
欧拉筛 O(n)线性筛
void oulashai(){
for(int i=2;i<=n;i++){
if(!vis[i]) pri[++tot]=i;
for(int j=1;j<=tot;j++){
int m=i*pri[j];
if(m>n) break;
vis[m]=1;
if(!i%pri[j]) break;
}
}
}
埃氏筛 O(n*loglogn)
void aishai(){
for(int i=2;i<=n;i++){
if(!vis[i]) pri[++tot]=i;
for(long long j=i*i;j<=n;j+=i)
vis[j]=1;
}
}
快速幂
long long pow_mod(int a,int b){//a^b
long long ans=1;
while(b){
if(b&1) ans=ans*a;
a*=a;
b>>=1;
}
return ans;
}
gcd有关
gcd
代码
风骚的一行写法
int gcd(int a,int b){
return a%b==0 ? b : gcd(b,a%b);
}
证明
令a=kb+r r=a-kb r=a%b
设 d是a,b的公共约数,d|b,d|a;
r=a-kb
r/d=a/d-kb/d 普通意义下的除法
a/d,kb/d是整数--->r/d是整数
d是r的约数
所以gcd(a,b)==gcd(b,r)==gcd(b,a%b)
exgcd
代码
void exgcd(int a,int b,int &x,int &y){
if(b==0){
x=1;
y=0;
return ;
}
exgcd(b,a%b,x,y);
int tmp=y;
y=x-(a/b)*y;
x=tmp;
return ;
}
证明:
ax+by=gcd(a,b)
bx1+(a%b)y1=gcd(b,a%b)
bx1+(a-(a/b)*b)y1=gcd(b,a%b)//整除
bx1+ay1-(a/b)*by1=gcd(b,a%b)
ay1+b(x1-(a/b)*y1)=gcd(b,a%b) ①
ax+by=gcd(a,b) ②
①== ②
x=y1,y=x1-(a/b)*y1;
序列问题
错排
推导式
d[1]=0,d[2]=1;
d[n]=(n-1)*(d[n-1]+d[n-2])
定义
考虑一个有n个元素的排列,若一个排列中所有的元素都不在自己原来的位置上,那么这样的排列就称为原排列的一个错排。 n个元素的错排数记为D(n)。 研究一个排列错排个数的问题,叫做错排问题或称为更列问题。
证明:
对于第n个元素,考虑把它放在第k位上(k有n-1中方案)
记k位上本来的元素为g
若g放在n上 除了n,k以外的n-2位元素错排
若g不放在n上 除了n以外的n-1位元素错排 (此时g不能放的位置是n而不是k,k位已经被n占据,不在考虑范围内)
d[n]=(n-1)*(d[n-1]+d[n-2])
由于d值的大小可以表示概率的大小,因此不需考虑k放在n位和不放在n位的概率问题。